3

I have a problem passing parameters if the parameters may contain wildcards and/or spaces, if those parameters are optional. Since this sounds pretty abstract, let's have a small example: The following shell script some_command.sh expects 2 or 3 arguments. The first argument is supposed to be a command line switch, the second argument is optional and, if present, must be a command line switch of the form --NAME=VALUE, and the final argument is required and can be anything:

#!/bin/bash
# This is file some_command.sh
# Synopsis:
# some_command.sh --switch1=val1 [--switch2=val2] arg
echo "switch1: $1"
shift
if [[ "$1" == --*=* ]]
then
  echo "switch2 ($1) detected"
  shift
fi
echo argument is ${1:?argument missing}

Let's assume that I am calling some_command.sh from some other script, caller.sh, in the following way:

#!/bin/bash
# This is file caller.sh
if [[ ${1:-x} == x ]]
then
  switch="--abc=long argument"
else
  switch=""
fi
some_command.sh "--exclude=*~" "$switch" arg

Note the quoting. The quotes around --exclude are necessary, because the wildcard expression must not be expanded by the shell, and the quotes around "$switch" are necessary, because $switch may contain text having spaces, and the argument must not be broken up on the spaces.

The intention is that if we execute caller.sh x, this should result into

some_command.sh "--exclude=*~" "--abc=long argument"  arg

and if we execute, say, caller.sh y, this should turn into

some_command.sh "--exclude=*~" arg

The caller.sh which I have provided here, does not work correctly, because in the latter case, it would execute

some_command.sh "--exclude*~" "" arg

which is incorrect.

I tried to prefix the command with eval. While this would solve the problem with $switch, it would also remove the quotes around "--exclude", and the wildcards would be evaluated by the shell.

I guess I could go on with the eval, and just use an extra level of quoting, i.e. "\"--exclude*~\"", but this is an awful solution. I wonder if somebody has a cleaner way to do it.

In case you wonder why I came to this question: I stumbled over the problem when writing scripts invoking zip, and since these scripts should be able to cope with spaces in file names.

BTW, the problem, as stated, occurs with bash and zsh. I am also interested in clever solutions, which work only with one of those shells.

3

Use an array since that can expand to a variable number of arguments:

#!/bin/bash
# This is file caller.bash
switch=()
if [[ ${1-x} == x ]]
then
  switch=("--abc=long argument")
fi
some_command.sh "--exclude=*~" "${switch[@]}" arg

Or you could use the ${var+...} syntax:

#!/bin/sh
# This is file caller.sh
unset switch
if [ "${1-x}" = x ]
then
  switch="--abc=long argument"
fi
some_command.sh "--exclude=*~" ${switch+"$switch"} arg

Note that with zsh, you can do:

#!/bin/zsh
switch=
if [ "${1-x}" = x ]
then
  switch="--abc=long argument"
fi
some_command.sh "--exclude=*~" $switch arg

zsh doesn't do split+glob upon parameter expansion, but it does empty removal which is what you want here.

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.