How do I pass on script arguments that contain quo

2019-01-23 03:00发布

问题:

I'm trying to write a script notify-finish that can be prepended to any command. When done, it will run the command given by the arguments following, then email the user when the command is complete. Here's what I have:

PROG=$1
# Run command given by arguments
$@
ECODE=$?
echo -e "Subject: `hostname`: $PROG finished\r\nTo: <$USER>\r\n\r\nExited with $ECODE\r\n" | sendmail $USER

This works most of the time, but when arguments contain spaces, the quoting is stripped off.

Working example:

notify-finished rsync -avz source/ user@remote:dest/

Failing example:

notify-finished rsync -avz -e 'ssh -c blowfish' source/ user@remote:dest/

In the second case, $@ is expanded out to rsync -avz -e ssh -c blowfish source user@remote:dest/, missing the single quotes. It does not work with double-quotes either, nor with $*.

After reading other posts I tried putting the command in an array, but I get the exact same issue:

CMD=(notify-finished rsync -avz -e 'ssh -c blowfish' source/ user@remote:dest/)
${CMD[@]}

How do I make this work for all arguments?

回答1:

Use "$@" with quotes:

prog="$1"
"$@"
ecode="$?"
echo "$prog exited with $ecode"

This will pass each argument exactly as it was received. If you don't include the quotes, each element will be split according to $IFS:

  • "$@" is like "$1" "$2" "$3" ..., passing each element as a separate argument.
  • "$*" is like "$1 $2 $3 ...", passing all elements concatenated as a single argument
  • $* and $@ is like $1 $2 $3 ..., breaking up each element on whitespace, expanding all globs, and passing each resulting word as a separate element ($IFS).

The same is true for arrays, such as "${array[@]}" and "${array[*]}"



回答2:

Put double-quotes around your variable substitutions to keep them from being parsed (note that this applies to all variables: $@, $1, and $PROG). Also: don't put a $ before a variable name when assigning to it; use # for comments; and, on the last line, the single-quotes will prevent variables from being substituted at all.

PROG="$1"
shift
# Run program below
"$PROG" "$@"
ECODE=$? # note: this will always be a number, so it doesn't have to be protected with double-quotes
echo -e "Subject: $(hostname): $PROG finished\r\nTo: <$USER>\r\n\r\nExited with $ECODE\r\n' | sendmail "$USER"


回答3:

Your script should be like this:

# Run program below
PROG="$@"
${PROG}
ECODE=$?
echo -e "Subject: $(hostname): ${PROG} finished\r\nTo: <$USER>\r\n\r\nExited with $ECODE\r\n"

Final echo command needs double quotes instead of single quote ' to interpret and expand $ variables.

And you should be enclosing command line argument in double quotes as well like this:

notify-finished "rsync -avz -e 'ssh -c blowfish' source/ user@remote:dest/"


标签: bash quoting