bashshellquoting

How can you double quote a shell variable in a command line only if the variable is set?


I need to execute a bash command where, only if a variable is set, I want to include that variable on the command line. The trick is that the variable could have a space in it, and the variable must be passed as a single argument.

The following command will not do the correct thing if foo is unset (but will if it is set), it will pass the command bar an empty argument since $FOO is not set but is double quoted.

FOO=""
bar "$FOO"

The following command fixes the flaw of $FOO being unset, but adds the flaw of passing two arguments if $FOO has a space in it.

FOO="one two"
bar $FOO

I tried using bar ${FOO+"}$FOO${FOO+"} but while it stuck quotes on there, it still passed two arguments. I imagine that if using eval with sufficiently convoluted quoting of all of the other arguments (not listed here) that would be impossible to get right without a lot of testing, the problem could also be solved.

Obviously you could say if [ "$Z" ]; then bar "$Z"; else bar; fi but this does not scale.

What is the best way to accomplish this?


Solution

  • Use ${var:+"$var"}

    This works because the alternate value respects quotes:

    unset var
    set -- ${var:+"$var"}
    echo "Unset: $# args"
    
    var=""
    set -- ${var:+"$var"}
    echo "Blank: $# args"
    
    var="foo bar"
    set -- ${var:+"$var"}
    echo "Multiple words: $# args"
    

    outputs

    Unset: 0 args
    Blank: 0 args
    Multiple words: 1 args
    

    (To pass empty strings but not unset values, use ${var+"$var"} without the colon)