call program with arguments from an array containing items from another array wrapped in double quotes

丶灬走出姿态 提交于 2019-12-11 17:23:58

问题


(This is a more specific version of the problem discussed in bash - expand arguments from array containing double quotes .)

I want bash to call cmake with arguments from an array with double quotes which itself contain items from another array. Here is an example for clarification:

cxx_flags=()
cxx_flags+=(-fdiagnostics-color)
cxx_flags+=(-O3)

cmake_arguments=()
cmake_arguments+=(-DCMAKE_BUILD_TYPE=Release)
cmake_arguments+=("-DCMAKE_CXX_FLAGS=\"${cxx_flags[@]}\"")

The arguments shall be printed pretty like this:

$ echo "CMake arguments: ${cmake_arguments[@]}"
CMake arguments: -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-fdiagnostics-color -O3"

Problem

And finally cmake should be called (this does not work!):

cmake .. "${cmake_arguments[@]}"

It expands to (as set -x produces):

cmake .. -DCMAKE_BUILD_TYPE=Release '-DCMAKE_CXX_FLAGS="-fdiagnostics-color' '-O3"'

Workaround

echo "cmake .. ${cmake_arguments[@]}" | source /dev/stdin

Expands to:

cmake .. -DCMAKE_BUILD_TYPE=Release '-DCMAKE_CXX_FLAGS=-fdiagnostics-color -O3'

That's okay but it seems like a hack. Is there a better solution?

Update

If you want to iterate over the array you should use one more variable (as randomir and Jeff Breadner suggested):

cxx_flags=()
cxx_flags+=(-fdiagnostics-color)
cxx_flags+=(-O3)
cxx_flags_string="${cxx_flags[@]}"

cmake_arguments=()
cmake_arguments+=(-DCMAKE_BUILD_TYPE=Release)
cmake_arguments+=("-DCMAKE_CXX_FLAGS=\"$cxx_flags_string\"")

The core problem remains (and the workaround still works) but you could iterate over cmake_arguments and see two items (as intended) instead of three (-DCMAKE_BUILD_TYPE=Release, -DCMAKE_CXX_FLAGS="-fdiagnostics-color and -O3"):

echo "cmake .. \\"
size=${#cmake_arguments[@]}
for ((i = 0; i < $size; ++i)); do
    if [[ $(($i + 1)) -eq $size ]]; then
        echo "    ${cmake_arguments[$i]}"
    else
        echo "    ${cmake_arguments[$i]} \\"
    fi
done

Prints:

cmake .. \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_CXX_FLAGS="-fdiagnostics-color -O3"

回答1:


It seems that there's another layer of parsing that has to happen before cmake is happy; the | source /dev/stdin handles this, but you could also just move your CXX flags through an additional variable:

#!/bin/bash -x 

cxx_flags=()
cxx_flags+=(-fdiagnostics-color)
cxx_flags+=(-O3)
CXX_FLAGS="${cxx_flags[@]}"

cmake_arguments=()
cmake_arguments+=(-DCMAKE_BUILD_TYPE=Release)
cmake_arguments+=("'-DCMAKE_CXX_FLAGS=${CXX_FLAGS}'")
CMAKE_ARGUMENTS="${cmake_arguments[@]}"

echo "CMake arguments: ${CMAKE_ARGUMENTS}"

returns:

+ cxx_flags=()
+ cxx_flags+=(-fdiagnostics-color)
+ cxx_flags+=(-O3)
+ CXX_FLAGS='-fdiagnostics-color -O3'
+ cmake_arguments=()
+ cmake_arguments+=(-DCMAKE_BUILD_TYPE=Release)
+ cmake_arguments+=("'-DCMAKE_CXX_FLAGS=${CXX_FLAGS}'")
+ CMAKE_ARGUMENTS='-DCMAKE_BUILD_TYPE=Release '\''-DCMAKE_CXX_FLAGS=-fdiagnostics-color -O3'\'''
+ echo 'CMake arguments: -DCMAKE_BUILD_TYPE=Release '\''-DCMAKE_CXX_FLAGS=-fdiagnostics-color -O3'\'''
CMake arguments: -DCMAKE_BUILD_TYPE=Release '-DCMAKE_CXX_FLAGS=-fdiagnostics-color -O3'

There is probably a cleaner solution still, but this is better than the | source /dev/stdin thing, I think.




回答2:


You basically want cxx_flags array expanded into a single word.

This:

cxx_flags=()
cxx_flags+=(-fdiagnostics-color)
cxx_flags+=(-O3)

flags="${cxx_flags[@]}"

cmake_arguments=()
cmake_arguments+=(-DCMAKE_BUILD_TYPE=Release)
cmake_arguments+=(-DCMAKE_CXX_FLAGS="$flags")

will produce the output you want:

$ set -x
$ echo "${cmake_arguments[@]}"
+ echo -DCMAKE_BUILD_TYPE=Release '-DCMAKE_CXX_FLAGS=-fdiagnostics-color -O3'

So, to summarize, running:

cmake .. "${cmake_arguments[@]}"

with array expansion quoted, ensures each array element (cmake argument) is expanded as only one word (if it contains spaces, the shell won't print quotes around it, but the command executed will receive the whole string as a single argument). You can verify that with set -x.

If you need to print the complete command with arguments in a way that can be reused by copy/pasting, you can consider using printf with %q format specifier, which will quote the argument in a way that can be reused as shell input:

$ printf "cmake .. "; printf "%q " "${cmake_arguments[@]}"; printf "\n"
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS=-fdiagnostics-color\ -O3

Note the backslash which escapes the space.



来源:https://stackoverflow.com/questions/45338369/call-program-with-arguments-from-an-array-containing-items-from-another-array-wr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!