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.)

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 -- "$@"
  • 1
    @Jesse_b It's a minor change and the rest works the same, but yes, that made it work for me – millimoose Jan 11 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 Jan 11 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 Jan 11 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 – Isaac Jan 11 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. – glenn jackman Jan 13 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.).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.