2

I am executing the command foo --bar "" --baz, i.e. I'm calling foo with three arguments:

  1. --bar
  2. an empty string
  3. --baz

This works.

Now, I would like to store that parameter sequence in a shell variable before passing it to foo (because I'm calling foo dozens of time with a varying list of parameters and I want that variable to hold the parameters common to all invocations), something along the lines of

PARAMS='--bar "" --baz'; foo ${PARAMS}

But I can't find a way to do that in a way that preserves all three parameters including the empty one. For example, the version above will pass three parameters to foo, but the middle one will be the string "" (two double quotes) instead of an empty string.

Other approaches I tested where:

  • PARAMS="--foo \"\" --bar"; foo ${PARAMS} also turns the second parameter into the string ""
  • PARAMS="--foo --bar"; foo ${PARAMS} removes the second parameter
  • PARAMS="--foo --bar"; foo "${PARAMS}" coalesces the parameters into a single one --foo --bar
Dreamer
  • 1,139
  • 9
  • 18
  • 1
    Use an array. `PARAMS=( --bar "" --baz ); foo "${PARAMS[@]}"`. – tshiono Aug 29 '23 at 06:40
  • 3
    Seems you are not yet familiar with [BashFAQ #50](http://mywiki.wooledge.org/BashFAQ/050), and specifically **5. I'm constructing a command based on information that is only known at run time**. And recall `eval` is just one character away from `evil` - use with caution. – David C. Rankin Aug 29 '23 at 06:41
  • Why not put the empty string into a new variable and hand that one to `foo`? Like `empty=""` and then `PARAMS='--foo $empty --bar'`. It might then work to use `eval "foo $PARAMS"`. I couldn't test this right now though. – CloudWatcher Aug 29 '23 at 06:49
  • Note that ALL_UPPERCASE variable names (like `PARAMS`) are best avoided because there is a danger of clashes with the large number of special ALL_UPPERCASE variables that are used in shell programming. See [Correct Bash and shell script variable capitalization](https://stackoverflow.com/q/673055/4154375). – pjh Aug 29 '23 at 10:00
  • 1
    Does this answer your question? [How can I store a command in a variable in a shell script?](https://stackoverflow.com/questions/5615717/how-can-i-store-a-command-in-a-variable-in-a-shell-script) – pjh Aug 29 '23 at 10:10

4 Answers4

3

You can't do that with variables and keep your sanity, but with a shell function, it becomes trivial:

call_foo() {
  foo --bar '' --baz "$@"
}

call_foo with 'additional parameters'

"$@" expands to all parameters of the function, while preserving the original field splitting of the passed arguments.

This solution will not only work for bash, but any POSIX compliant shell.

knittl
  • 246,190
  • 53
  • 318
  • 364
3

Use an array:

PARAMS=(--bar "" --baz)
foo "${PARAMS[@]}"
Paolo
  • 21,270
  • 6
  • 38
  • 69
1

Since you're using Bash, an array variable is the standard solution.

Assign it like this:

PARAMS=(--bar "" --baz)

And expand it like this (the quotes are important!):

foo "${PARAMS[@]}"

See the section Arrays in the Bash manual:

Any element of an array may be referenced using ${name[subscript]}. The braces are required to avoid conflicts with the shell’s filename expansion operators. If the subscript is ‘@’ or ‘*’, the word expands to all members of the array name. These subscripts differ only when the word appears within double quotes. If the word is double-quoted, ${name[*]} expands to a single word with the value of each array member separated by the first character of the IFS variable, and ${name[@]} expands each element of name to a separate word. When there are no array members, ${name[@]} expands to nothing.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
0

Your PARAMS variable is actually holding a piece of shell syntax. It requires eval to process as such:

eval foo "$PARAMS"

Unfortunately, eval is a dangerous function which can render a script insecure.

If everything in PARAMS was carefully constructed by your script, such that no part of the material is an external input (in particularly, an untrusted input from "the wild"), you shouldn't have any problems.

knittl's answer is the best solution for this situation: calling a function with certain fixed parameters plus varying ones. It avoids eval; if you can avoid eval, you almost always should.

Kaz
  • 55,781
  • 9
  • 100
  • 149