Bash : command line with optional arguments

argumentsbashquoting

I'm running this kind of code:

#!/usr/bin/env bash
set -u
exclude1='--exclude=/path/*'
exclude2='--exclude=/path with spaces/*'
exclude3=''        # any 'exclude' can be empty
tar -czf backup.tgz "$exclude1" "$exclude2" "$exclude3" 2>&1 | grep -i 'my_reg_exp' > error.log
RESULT=("${PIPESTATUS[@]}")
... etc ...

When I run this code, I get this error:

tar: : Cannot stat: No such file or directory

This is because "$exclude3" is translated as an empty argument. Exactly as if I did this:

tar -czf backup.tgz "$exclude1" "$exclude2" ''

One way to avoid this error is to remove the double quotes around $excludeX. But this is a problem if $excludeX contains any space or other strange characters.

Another way would be to use eval but because I need to keep the double quotes, I don't see how to suppress the quotes AND the empty arguments when needed.

The only solution I found is to construct the command-line with string concatenation:

CMD='tar -czf backup.tgz'
if [[ -n "$exclude1" ]]; then CMD+=" \"$exclude1\" "; fi
if [[ -n "$exclude2" ]]; then CMD+=" \"$exclude2\" "; fi
if [[ -n "$exclude3" ]]; then CMD+=" \"$exclude3\" "; fi
eval $CMD 2>&1 | grep -i 'my_reg_exp' > error.log
RESULT=("${PIPESTATUS[@]}")
... etc ...

Anyone have a smarter idea?

Best Answer

tar -czf backup.tgz "$exclude1" "$exclude2" ${exclude3+"$exclude3"} 2>&1

${exclude3+"$exclude3"} expands to nothing, if $exclude3 is unset, and to "$exclude3", if it is set.

(and similarly for the other variables that are potentially unset.)

Note that there is a difference between an unset variable and a variable that is set to the empty string, so you should use

unset exclude3

instead of

exclude3=''
Related Question