如何传递包含引号/空格的脚本参数?

29

我正在尝试编写一个名为notify-finish的脚本,该脚本可用于任何命令前。当完成后,它将运行后面给定参数的命令,并在命令完成时向用户发送电子邮件。这是我的代码:

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

这个方法大部分时间可以工作,但当参数包含空格时,引号会被去掉。

有效的示例:

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

失败的示例:

notify-finished rsync -avz -e 'ssh -c blowfish' source/ user@remote:dest/
在第二种情况下,$@ 被展开为 rsync -avz -e ssh -c blowfish source user@remote:dest/,缺少单引号。它也不能使用双引号或 $*。通过阅读其他帖子,我尝试将命令放入数组中,但我遇到了完全相同的问题。
CMD=(notify-finished rsync -avz -e 'ssh -c blowfish' source/ user@remote:dest/)
${CMD[@]}

如何使此代码适用于所有参数?

2个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
30

使用带引号的 "$@"

prog="$1"
"$@"
ecode="$?"
echo "$prog exited with $ecode"
这将会按照接收到的参数精确地传递每个参数。如果不包含引号,则每个元素将根据$IFS进行拆分:
  • "$@" 就像 "$1" "$2" "$3" ...,将每个元素作为单独的参数传递。
  • "$*" 就像 "$1 $2 $3 ...",将所有元素连接成一个单一的参数传递。
  • $*$@ 就像 $1 $2 $3 ...,将每个元素按空格拆分,扩展所有通配符,并将每个结果单词作为单独的元素($IFS)传递。

对于数组也是同样的规则,例如 "${array[@]}""${array[*]}"


4
这是针对双引号内的 $@ 的特殊行为。 - glenn jackman
这个完美地运行了。一段时间以前,我将$@分配给了ARGS,就像ARGS=$@,然后执行$PROG $ARGS,所以这不会起作用。谢谢。 - axon
4
如果您想将参数存储在一个变量中,可以使用数组变量:ARGS=("$@"); "$PROG" "${ARGS[@]}" - Gordon Davisson

4

在变量替换周围加上双引号,以防止它们被解析(注意,这适用于所有变量:$@$1$PROG)。 另外:在为变量赋值时不要在变量名前面加上$;使用#进行注释;最后一行使用单引号将完全阻止变量的替换。

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"

如果你要断开第一个参数,请在使用"$@"之前不要忘记执行shift - glenn jackman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,