0

My goal is to call a script with an variable as it's arguments. For example, I have the following two files:

print_args.sh:
    echo "Length: $#"
    for i in "$@"; do echo "$i"; done

caller.sh:
    ARGS="foo \"Hello, World\" bar"
    ./test.sh $ARGS

When I run:

./print_args.sh foo "Hello, World" bar

print_args.sh get's called with 3 arguments:

Length 3
foo
Hello, World
bar

However, when running it via caller.sh instead I get 4 args:

Length: 4
foo
"Hello,
World"
bar

What's going on here? How can I get caller.sh to perform as expected?

Note: I don't have control over ARGS it's passed in as an environment variable.

Aaron N. Brock
  • 4,276
  • 2
  • 25
  • 43
  • Read this answer: [bash - variable containing multiple args with quotes](https://stackoverflow.com/a/7454624/6176817). – PesaThe Jul 13 '18 at 07:41
  • When you say you have no control over `ARGS`, can you at least specify that a different format is used to separate the arguments? – Tom Fenech Jul 13 '18 at 07:50
  • What about documenting that the environment variable should be set like `ARGS=(foo "Hello, World" bar)`, is that also not an option? – David Conrad Jul 13 '18 at 08:29
  • Following on from my previous comment, I added a section to my answer to show what I meant. – Tom Fenech Jul 13 '18 at 08:41

1 Answers1

3

How can I get test_caller.sh to perform as expected?

Use an array:

#!/bin/bash

args=( foo "Hello, World" bar )
./test.sh "${args[@]}"

If you pass an unquoted variable, then word splitting occurs on its contents before it is passed to ./test.sh. You are essentially calling:

'./test.sh' 'foo' '"Hello,' 'World"' 'bar'

Where the single quotes are used to indicate the start and end of words. The double quotes have no syntactic meaning, they are just characters in a string at this point.


If you can convince the users of your script to change the format of the environment variable, then you could split it based on a different separator:

ARGS="foo:Hello World:bar"
set -f # disable glob expansion
IFS=: args=( $ARGS )
set +f # re-enable it

Now you have an array args which you can use as above.


One (really nasty, dangerous) way to get your string into an array would be to use the notorious eval:

readarray -t args < <(eval printf '%s\\n' $ARGS)

Now you have an array args which you can use as in the previous examples.

But this is a big security problem:

ARGS="foo \"Hello, World\" bar \$(ls -l)"

Replace ls -l with any command that you want to execute...

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141