下面的脚本在 bash 中有效,但在 zsh 中无效。我认为这是因为在变量中OPTS
,我正在“扩展”(不确定这是否是正确的词)变量$EXCLUDE
,并且这种语法在zsh中不起作用。我如何替换该行以使其在 zsh 上工作?
SRC="/path_to_source"
DST="/path_to_dest"
EXCLUDE=".hg"
OPTS="-avr --delete --progress --exclude=${EXCLUDE} --delete-excluded"
rsync $OPTS $SRC $DST
答案1
这里的问题是$OPTS
没有在命令行上分成几个参数rsync
。在zsh
语法中,使用:
rsync ${=OPTS} $SRC $DST
(另一种方法是使用选项全局模拟标准 shell 行为-o shwordsplit
...)
从联机帮助页:
[in] 中常见的一个区别
zsh
是替换到命令行上的变量不会拆分为单词。请参阅SH_WORD_SPLIT
中“参数扩展”部分中shell 选项的描述zshexpn(1)
。在 中zsh
,您可以显式请求拆分(例如${=foo}
),或者当您希望变量扩展到多个单词时使用数组。请参阅 中的“数组参数”部分zshparam(1)
。
答案2
问题不是来自$EXCLUDE
,而是来自$OPTS
。您的脚本依赖于 发生的分词$OPTS
。这是一个坏主意;例如,如果您更改$EXCLUDE
为包含模式并--exclude=$EXCLUDE
匹配当前目录中的文件,或者如果您更改$EXCLUDE
为包含空格,则它将失败。
$OPTS
是一个单词列表,而不是一个单词。如果您只需要脚本在 ksh、bash 和 zsh 中工作,请将其设为数组:
SRC="/path_to_source"
DST="/path_to_dest"
EXCLUDE=".hg"
OPTS=(-avr --delete --progress --exclude="$EXCLUDE" --delete-excluded)
rsync "${OPTS[@]}" "$SRC" "$DST"
如果您只想让脚本在 zsh 中工作,您可以简化最后一行:
rsync $OPTS $SRC $DST
如果您希望脚本在每个 shell 中运行,则需要使用位置参数。它们是唯一可用的阵列。
set -- -avr --delete --progress --exclude="$EXCLUDE" --delete-excluded
rsync "$@" "$SRC" "$DST"
如果您希望能够使用 zsh 方便地运行一个 shell 脚本,请将此行放在开头 — 它告诉 zsh 的行为类似于 ksh,并且在其他 shell 上是无操作的。
emulate ksh >/dev/null 2>/dev/null || true
答案3
zsh
要完成 Stéphane Gimenez 的回答(我还不能发表评论),一种解决方案是通过检查您是否正在使用然后将其转换$OPTS
为这样的数组,使您的脚本可移植到 bash
[ -n "${ZSH_VERSION:-}" ] && set -A OPTS ${=OPTS}
然后你的 rsync 命令就可以工作了。