假设我有一个脚本正在执行:
some-command "$var1" "$var2" ...
而且,如果为var1
空,我宁愿将其替换为空字符串,而不是空字符串,以便执行的命令是:
some-command "$var2" ...
并不是:
some-command '' "$var2" ...
有没有比测试变量并有条件包含它更简单的方法?
if [ -n "$1" ]; then
some-command "$var1" "$var2" ...
# or some variant using arrays to build the command
# args+=("$var1")
else
some-command "$var2" ...
fi
是否有参数替换可以在 bash、zsh 等中扩展为空?我可能仍然想在其余参数中使用通配符,因此禁用它并取消引用变量不是一个选项。
答案1
符合 Posix 标准的 shell和重击有 ${parameter:+word}
:
如果范围未设置或为 null,应替换为 null;否则,扩展单词(或者一个空字符串,如果单词被省略)应被替换。
所以你可以这样做:
${var1:+"$var1"}
并且已经var1
被检查,并且"$var1"
如果它被设置并且非空(使用普通的双引号规则)则被使用。否则它就会膨胀成无。请注意,只有内这里引用的是部分内容,并非全部内容。
在 zsh 中也同样有效。您必须重复该变量,因此它并不理想,但它的结果完全符合您的要求。
如果您希望将 set-but-empty 变量扩展为空参数,请改用${var1+"$var1"}
。
答案2
zsh
当您省略引号时,默认情况下会执行以下操作:
some-command $var1 $var2
实际上,在 zsh 中围绕参数扩展仍然需要引号的唯一原因是避免这种行为(空删除),因为zsh
没有当您不引用参数扩展时影响其他 shell 的其他问题(隐式 split+glob)。
如果禁用 split 和 glob,您可以对其他类似 POSIX 的 shell 执行相同的操作:
(IFS=; set -o noglob; some-command $var1 $var2)
现在,我认为如果你的变量可以有 0 或 1 值,它应该是一个数组而不是标量变量并使用:
some-command "${var1[@]}" "${var2[@]}"
并使用var1=(value)
何时var1
包含一个值、var1=('')
何时包含一个空值以及var1=()
何时包含不价值。
答案3
-n
我在 bash 脚本中使用 rsync 遇到了这个问题,该脚本在带或不带切换干运行的情况下启动命令。事实证明,rsync 和许多 gnu 命令将其''
作为有效的第一个参数,并且其行为与不存在时的行为不同。
这花了相当长的时间来调试,因为空参数几乎完全不可见。
rsync 列表中的某人向我展示了一种避免此问题的方法,同时也大大地简化我的编码。如果我理解正确的话,这是@Stéphane Chazelas 最后建议的变体。
在许多单独的变量中构建命令参数。这些可以按照适合问题的任何顺序或逻辑进行设置。
然后,最后,使用变量构造一个数组,其中所有内容都位于适当的位置,并将其用作实际命令的参数。
这样,命令仅在代码中的一个位置发出,而不是针对参数的每个变体重复发出。
使用此方法,任何空变量都会消失。
我知道使用 eval 是非常不受欢迎的。我不记得所有的细节,但我似乎需要它来让事情以这种方式工作 - 与处理带有嵌入空白的参数有关。
例子:
dry_run=''
if [[ it is a test run ]]
then
dry_run='-n'
fi
...
rsync_options=(
${dry_run}
-avushi
${delete}
${excludes}
--stats
--progress
)
...
eval rsync "${rsync_options[@]}" ...