4

I want to call a program with arguments built from an array in bash.

I want bash to call:

echo -arg1=simple -arg2="some spaces"

from array=(echo -arg1=simple "-arg2=\"some spaces\"") or similar (I can adjust the way the items are created).

Problem

With "${array[@]}" bash calls:

echo -arg1=simple '-arg2="some spaces"'

But I do not want the single quotes. How to build and expand the array correctly?

Example code

#!/bin/bash
set -x

array=()
array+=(echo)
array+=(-arg1=simple)
array+=("-arg2=\"some spaces\"")

"${array[@]}"
"${array[*]}"
${array[@]}
${array[*]}

Resulting calls

echo -arg1=simple '-arg2="some spaces"'
'echo -arg1=simple -arg2="some spaces"'
echo -arg1=simple '-arg2="some' 'spaces"'
echo -arg1=simple '-arg2="some' 'spaces"'
bibermann
  • 77
  • 1
  • 7

2 Answers2

5

"${array[@]}" is correct. The -x option simply chooses a canonical way to display values that require quoting, and '-arg2="some spaces"' is equivalent to "-arg2=\"some spaces\"".

chepner
  • 497,756
  • 71
  • 530
  • 681
3

You can simply do it like this, no need to keep echo inside the array:

#!/bin/bash -x

array=()
array+=(-arg1=simple)
array+=(-arg2="some spaces")

echo "${array[@]}"

This results with a call to echo which receives two words as arguments, -arg1=simple and -arg2="some spaces", as if you wrote:

echo -arg1=simple -arg2="some spaces"

Alternatively, you can define your array in one line, with declare:

declare -a array=(-arg1=simple -arg2="some spaces")

To check how it will be expanded, you can use printf (we use == here just to clearly show the beginning and ending of each argument):

$ printf "==%s==\n" "${array[@]}"
==-arg1=simple==
==-arg2=some spaces==

Note the importance of quotes around ${array[@]}. They ensure that each element in the array is expanded into only one word (like if quoted in shell before expansion). Compare that with:

$ printf "==%s==\n" ${array[@]}
==-arg1=simple==
==-arg2=some==
==spaces==

Update. If you want to expand it to exactly -arg2="some spaces" (not sure why you would want it, though), just wrap it inside a single quotes on definition:

$ declare -a array=(-arg1=simple '-arg2="some spaces"')
$ echo "${array[@]}"
-arg1=simple -arg2="some spaces"
$ printf "==%s==\n" "${array[@]}"
==-arg1=simple==
==-arg2="some spaces"==
randomir
  • 17,989
  • 1
  • 40
  • 55
  • Thank you for the tip with printf, but I want it expand to `-arg2="some spaces"` and not `-arg2=some spaces` – bibermann Jul 26 '17 at 19:18
  • When I wrap it like so: `echo "echo ${array[@]}" | source /dev/stdin` bash seems to call `echo -arg1=simple '-arg2=some spaces'` which is not exactly what I'm searching but cmake seems to accept that (`cmake .. -bla -bla '-DCMAKE_CXX_FLAGS=-bla -bla'`). – bibermann Jul 26 '17 at 19:36
  • Your update would work, but in the real code `some spaces` is `${cxx_flags[*]}` and with the single quotes this variable does not get expanded :/ – bibermann Jul 26 '17 at 19:41
  • 1
    @bibermann, that's crucial detail. Please update the question, because that changes everything. And please tell up **exactly** what are you trying to do. Help us so we can help you! – randomir Jul 26 '17 at 19:42
  • 1
    CMake also accepts the version with single quotes I got in the question. I haven't tried that, it just looked wrong. Nevertheless, your update answeres the non-specific question, so I accept that. – bibermann Jul 26 '17 at 20:14
  • CMake does not like it anymore. I opened a new question: https://stackoverflow.com/questions/45338369/call-program-with-arguments-from-an-array-containing-items-from-another-array-wr – bibermann Jul 26 '17 at 22:20