如果为空,如何将带引号的变量扩展为空?


21

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

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等中,是否有参数替换无法扩展为空?我可能仍想在其余参数中使用globing,因此禁用该选项并取消引用该变量不是一种选择。


我知道我曾经看过并且可能以前用过,但是事实证明它很难搜索。现在,迈克尔表现的语法,我记得我第一次看到它的速度不够快,其中:unix.stackexchange.com/a/269549/70524unix.stackexchange.com/q/68484/70524
穆鲁

如果您知道这是某种参数替换,为什么不查看页面的参数扩展部分man?(-;
Philippos '18

1
@Philippos我当时不知道它是什么,只是我以前看过或使用过它。已知已知和未知已知。:(
大师

1
提及使用数组将参数保存在问题本身中的额外cookie点。
ilkkachu

Answers:


29

符合Posix的shellBash具有 ${parameter:+word}

如果参数未设置或为null,则应替换为null。否则,扩大(或如果一个空字符串被省略)应被取代。

因此,您可以执行以下操作:

${var1:+"$var1"}

并已var1检查,"$var1"如果已设置且为非空(使用普通的双引号规则),则将其使用。否则它将扩展为空。请注意,此处仅引用内部部分,而不引用整个内容。

在zsh中也一样。您必须重复该变量,因此它不是理想的,但它的效果完全符合您的期望。

如果希望将set-but-empty变量扩展为空参数,请${var1+"$var1"}改用。


1
对我来说足够好了。因此,在此,不引用全部内容,仅引用word部分内容。
muru

1
当问题被问及“ bash,zsh之类”(以及问与答档案)时,我进行了编辑以反映这是posix功能。即使您在我的评论后在问题中添加了/ bash标签。(-;
Philippos

2
我可以自由编辑:+和之间的区别+
ilkkachu

非常感谢您提供符合POSIX的解决方案!我尝试将sh/ dash用作潜在的扼流点脚本,因此当有人展示不借助Bash的情况下如何实现某事时,我总是会很感激。
JamesTheAwesomeDude

我一直在寻找相反的效果(如果以null开头,请扩展到其他内容)。事实证明,可以通过将+更改为-来反转此逻辑,例如:$ {empty_var:-replacement}
Alex Jansen

5

这是zsh默认情况下省略引号的作用:

some-command $var1 $var2

其实,为什么你还需要在报价参数周围的zsh扩张的唯一原因是为了避免行为(空去除)作为zsh没有影响其他炮弹其他问题,当你不报价参数扩展(隐裂+ glob)

如果禁用split和glob,则可以对其他类似POSIX的外壳执行相同的操作:

(IFS=; set -o noglob; some-command $var1 $var2)

现在,我认为如果您的变量可以具有0或1的值,则它应该是一个数组而不是标量变量,请使用:

some-command "${var1[@]}" "${var2[@]}"

并使用var1=(value)when var1是包含一个值,var1=('')何时包含一个空值以及var1=()何时包含值。


0

我在bash脚本中使用rsync遇到了这个问题,该脚本在启动命令时使用或不使用a -n来切换空运行。事实证明,rsync和许多gnu命令''作为有效的第一个参数,并且与不存在的情况不同。

由于空参数几乎完全不可见,因此调试花费了相当长的时间。

rsync列表上的某人向我展示了一种避免此问题的方法,同时还大大简化了我的编码。如果我正确理解,这是@StéphaneChazelas的最后建议的一种变体。

在许多单独的变量中构建命令参数。可以按照适合该问题的任何顺序或逻辑进行设置。

然后,最后,使用变量构造一个数组,并将所有内容放置在适当的位置,并将其用作实际命令的参数。

这样,命令仅在代码中的一个位置发出,而不是针对每个参数变体重复执行。

使用此方法,任何为空的变量都将消失。

我知道使用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[@]}" ...

这是做我在问题中描述的事情的更糟糕的方法(有条件地构建一组参数)。通过不加引号的变量,谁知道您遇到了什么问题。
muru

@muru你是对的,但我仍然需要这样的东西。我不太明白如何使用其他答案中的技术来修复它。看到正确的方式将所有代码片段组合在一起,真是太好了。稍后,我将使用Stephane的最后一个选项,因为这可能会实现我想要的功能。


刚回到这个。谢谢你的例子。这说得通。我将使用它。

我这里有一个类似的用例,但是您可以执行以下操作:if [“ $ dry” ==“ true”]; 然后dry =“-n”; fi ...因此,如果变量dry为true,则将其设置为-n,然后像往常一样构建rsync命令并使其如下所示:rsync $ {dry1:+“ $ dry1”} ...如果为null什么都不会发生,否则它将在您的命令中变为-n
Freedo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.