how to correctly use a bash variable containing multiple arguments (which may contain spaces)

Viewed 89

How can I pass a variable that contains spaces to a script so that the script processes them correctly?

My minimal example:

test.sh

#!/bin/bash
COUNTER=1
for i in "$@"; do echo "$COUNTER '$i'"; ((COUNTER++)); done

If I call the script directly with some arguments (escaped space) it works:

./test.sh 1 2 3\ 4 5

Output (as expected):

1 '1'
2 '2'
3 '3 4'
4 '5'

If I store the arguments into a variable, the backslash is not interpreted correctly anymore as a escape char.

TEST_ARGS="1 2 3\ 4 5"
./test.sh $TEST_ARGS

Output:

1 '1'
2 '2'
3 '3\'
4 '4'
5 '5'

I would like to get the same output like before. How can I reach it?

TEST_ARGS="1 2 3\ 4 5"
# how to call ./test.sh here?

My more detailed use case is, that I have some script, which prepares all flags for my other tools / scripts.

prepare_flags.sh (very minimalistic):

#!/bin/bash
echo 'TEST_ARGS="1 2 3\ 4 5"'

In my actual script I source the output of the prepare_flags.sh script in order to access TEST_ARGS:

source <( ./prepare_flags.sh )
./test.sh $TEST_ARGS # this fails
bash -c "./test.sh $TEST_ARGS" # this works

Is there any other solution for this except the bash -c?

2 Answers

Use an array, not a plain variable.

(Also, there is no reason to use ancient external tools like expr with Bash; ((++COUNTER)) would work just fine. (Also, it is better to avoid uppercase variable names, because those are (by convention) used for environment variables.))

test.sh:

for ((i = 1; i <= $#; ++i)); do echo "${i} '${!i}'"; done

Passing the arguments:

test_args=(1 2 '3 4' 5)
./test.sh "${test_args[@]}"

I just found some solution which seems to work so far:

TEST_ARGS="1 2 3\ 4 5"
bash -c "./test.sh $TEST_ARGS"

Output:

1 '1'
2 '2'
3 '3 4'
4 '5'
Related