如果引用的变量为空,如何将其扩展为空?

如果引用的变量为空,如何将其扩展为空?

假设我有一个脚本正在执行:

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[@]}" ...

相关内容