6

I want a script that will run another utility over some default paths if no parameters are passed to it; ideally I want this safe for paths that contain spaces.

So far I have script.sh:

#!/bin/sh
base=$(dirname "$0")
exec touch "${@:-"$base/aaa" "$base/bbb"}"

If I put this into a folder called "foo bar" and run it as:

foo\ bar/script.sh

I want it to should end up doing:

touch foo\ bar/aaa foo\ bar/bbb

i.e. create files "aaa" and "bbb" under "foo bar", the directory in which the script is located.

Instead I get the error

touch: cannot touch 'foo bar/aaa foo bar/bbb': No such file or directory

(If I pass in parameters to the script it seems to work fine. Presumably removing the outer quotes in the last command would reverse my cases.)

2 Answers 2

10

It appears you can't set default parameters in an expansion of ${@:-...}, and "${@:-"$base/aaa" "$base/bbb"}" is expanded as a single string.

If you want to set default parameters you might want to do this:

base=$(dirname -- "$0")
# test explicitly for no parameters, and set them.
if [ "$#" -eq 0 ]; then
    set -- "$base/aaa" "$base/bbb"
fi

Then, the "$@" magically quoted parameter substitution can happen unabated:

touch -- "$@"
10
  • 1
    @Jesse_b It's a minor change and the rest works the same, but yes, that made it work for me
    – millimoose
    Commented Jan 11, 2019 at 17:14
  • 2
    (I like to stick to being dash-compatible if possible for scripts so as to not have to pull in bash into Docker images unnecessarily.)
    – millimoose
    Commented Jan 11, 2019 at 17:20
  • About that first sentence, set --; echo ${@:-default}; set -- a b; echo ${@:-default}; works (i.e. prints default and then a b) in all shells I tried. Though I don't think you should be able to give a list as a default value, the syntax only mentions a single word there.
    – ilkkachu
    Commented Jan 11, 2019 at 17:27
  • 2
    Sorry, but it is possible to use several parameters in a default expansion, but only with shells that have arrays. See my answer
    – user232326
    Commented Jan 11, 2019 at 18:13
  • 1
    That's not strictly required. Variable assignments are not subject to word splitting and pathname expansion. You could do a=* and have a literal asterisk not a list of files. Of course, you must quote "$a" every time you use the variable. Commented Jan 13, 2019 at 12:32
6

It IS possible to use several parameters in a default expansion ${@-...},

like this:

#!/bin/bash
base=$(dirname "$0")
arr=("$base/aaa" "$base/bbb")

touch "${@:-"${arr[@]}"}"

But only on shells that have arrays (ksh, zsh, bash, etc.).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .