便携式检查shell变量空虚的方法

时间:2013-11-05 07:41:25

标签: bash sh dash-shell

在shell脚本中测试变量是否为空/未定义的可移植和规范方法是什么?它应该适用于所有类似sh的壳。我现在做的是:

if [ -z "$var" ] ; then
    ...

反过来,当变量不为空/未定义时执行某些操作:

if [ -n "$var" ] ; then
    ...

虽然这些对我现在编写的脚本起作用,我想知道一种方法,它可以在任何合理兼容的 sh -like shell 中使用,甚至在比使用GNU userland和 bash 破折号的Linux PC更加模糊的环境中。

我正在寻找一个在不同shell环境中经验丰富的人的“专家答案”,并且知道不同做事方式的缺陷,而不是“应该工作”的意见。

5 个答案:

答案 0 :(得分:5)

支架结构[...]是POSIX标准的一部分,几乎无处不在。因此,要测试变量是否为空,问题中使用的if案例是完美的:

if [ -z "$STRING" ]; then
    # $STRING is empty
fi

只要你记住:

始终引用变量

非引用变量受单词拆分影响,因此如果您使用[ -z $STRING ]$STRING周围没有引号)且$STRING包含空格,或者为空,则shell将会请参阅[ -z SEVERAL WORDS ][ -z ]这两个语法错误 - 即不是您想要的。

始终只使用一个等号

如果测试POSIX shell中的字符串是否为空,则另一种方法是使用=

if [ "$STRING" = "" ]; then
    # $STRING is empty
fi

但请注意,永远不会==内使用双等号([...])! (这仅适用于bash,或sh是指向bash的链接 - 但如果在sh链接到其他内容的其他计算机上运行,​​则不起作用。)如果你想使用双等号,你也应该有双括号[[...]](只适用于zshbash等,但是不像dash)那样普通的POSIX shell。

请勿在{{1​​}}

中使用&&||

可能在某些shell中工作,但它肯定不适用于所有shell。相反,对于“”,请写[...][...] && [...]。对于“”,请写[... -a ...][...] || [...]

古代的东西

有时,在非常古老的shell脚本中,你会看到如下所示的内容,以测试一个空变量。这仍然是完全有效的(但在我眼中看起来很笨重)而且我从来没有遇到需要这个的shell(不过,不可否认,我的经验在Linux之外是有限的 - 也许有BSD shell还需要这个吗?)< / p>

[... -o ...]

如果if [ ".$STRING" = "." ]; then # $STRING is empty fi 碰巧包含[...]$STRING以及-e的其他选项,则期间的前置是避免括号构造-o混淆。使用。通过预先设定一个期间,它将变为非期权。

我从未见过shell需要这种解决方法。 (Dash确实不需要它们。)所以忽略这一点可能是安全的。

答案 1 :(得分:3)

免责声明:我不认为自己是贝壳专家。

AFAIK,test命令以及-z-n标志是POSIX标准的一部分,因此您可以在许多平台上使用它们。

答案 2 :(得分:3)

重要的是要记住,一个空的shell变量与一个未设置的shell变量不同 - 尽管这种区别不能使用[ test ]的任何一种形式。 test所能做的就是报告变量扩展的价值 - 参数扩展决定了这一点。例如:

var=value
echo "${var+I am \$var and I am set. I am currently ${var:+not} empty.}"
###OUTPUT###
I am $var and I am set. I am currently not empty.

再次:

var=
echo "${var+I am \$var and I am set. I am currently ${var:+not} empty.}"
###OUTPUT###
I am $var and I am set. I am currently empty.

使用[ test ]

{   _nstr() { echo 'I just evaluated a ""null string!' ; }
    [ -z "$var" ] && _nstr
    [ -z "" ] && _nstr
    unset var    
    [ -z "$var" ] && _nstr
    echo "${var+I am \$var and I am set. I am currently ${var:+not} empty.}"
}
###OUTPUT###
I just evaluated a ""null string!
I just evaluated a ""null string!
I just evaluated a ""null string!

希望这能把它带回家:

var=value
[ -z "${var+}" ] && _nstr
###OUTPUT###    
I just evaluated a ""null string!

如果您想要可移植地确定变量是否为空,则可以执行此操作:

${var:+false} [ -n "${var+1}" ] && echo \$var is set and null

我认为你可以很好地利用shell的参数扩展来处理像这样的情况,甚至在某些情况下甚至可以一举两得(见第一个例子)。这绝对应该是便携式的。以下是来自开放组的POSIX shell指南的不完整复制/粘贴:http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02

${parameter:-word}

  • 使用默认值。如果参数为unsetnull,则应替换单词的扩展;否则,参数的值应替换。

${parameter:=word}

  • 指定默认值。如果参数为unsetnull,则应将字的扩展分配给参数。在所有情况下,参数的最终值应替换。只能以这种方式分配变量,而不是位置参数或特殊参数。

${parameter:?[word]}

  • 如果为空或未设置则指示错误。如果参数为unsetnull,则字的扩展(或者如果省略字则表示未设置的消息)应写入标准错误,并且shell以非零退出状态退出。否则,参数值应替换。交互式shell不需要退出。

${parameter:+word}

  • 使用替代价值。如果参数为unsetnull,则null将被替换;否则,词语的扩展将被替换。

<强>实施例

${parameter:-word}

  • 在此示例中,ls仅在xnullunset时执行。 (命令替换中解释了$( ls)命令替换表示法。) ${x:-$(ls)}

${parameter:=word}

% unset X
% echo ${X:=abc}
abc

编辑:

我刚刚注意到Ashish的单行,并且因为它感觉有点便宜,只是粘贴它,我想做某事。所以这是一个POSIX参数扩展集测试单行:

_JAIL="" ; echo "_JAIL is ${_JAIL:-unset or null.}"

嗯,我想还是便宜的。

EDIT2:

所以我只是注意到了这一点:“...虽然这些对我现在编写的脚本起作用,但我想知道一种方法,它可以在任何合理兼容的sh-like shell中工作,即使在某些比具有GNU userland和bash或dash的Linux PC更加模糊的环境......“

我很抱歉,我不是个人专家,但我首先选择了以下方法来定义重要的shell变量,这些方法可能是在通过一些相当专业的initramfs busybox脚本编写后可能会或可能没有传递的参数。它几乎可以在任何地方按预期工作。

它有点漂亮,因为如果用户没有定义它们而没有任何额外的工作来检查它们是否真的,那么你只将变量设置为默认条件。当然还有很多其他用途,我只是不够聪明,不能像我应该那样使用它们。

_VAR1=${1:-/default/path} ; _VAR2=${2:-"$_default_size"}

<强> EDIT3:

phs是正确的,指明他的方法测试的空变量不存在问题中提出的变量。根据是否使用中心结肠,该方法也可以这样做。我已经从下面的POSIX指南中复制了一个废话表来证明这一点。

{subs='substitute'; params='parameters'; assn='assign'; err='error'} 

         "$parameter" == |set && !null |set && null|  !set
       ------------------ ------------- ----------- ----------
      ${parameter:-word} | subs params | subs word | subs word
       ------------------ ------------- ----------- ----------
      ${parameter-word}  | subs params | subs null | subs word
       ------------------ ------------- ----------- ----------
      ${parameter:=word} | subs params | assn word | assn word
       ------------------ ------------- ----------- ----------
      ${parameter=word}  | subs params | subs null | assn word
       ------------------ ------------- ----------- ----------
      ${parameter:?word} | subs params | err; exit | err; exit
       ------------------ ------------- ----------- ----------
      ${parameter?word}  | subs params | subs null | err; exit
       ------------------ ------------- ----------- ----------
      ${parameter:+word} |  subs word  | subs null | subs null
       ------------------ ------------- ----------- ----------
      ${parameter+word}  |  subs word  | subs word | subs null

答案 3 :(得分:1)

便携式和规范性是两回事。

对于便携式,我在几个autoconf脚本中看到的测试是:

if test "x$MY_VAR" = x; then
  # ...
fi

这是对空虚的测试,而非缺席。

答案 4 :(得分:0)

除非你在测试变量中运行大代码,否则你不必进入if ladder梯形图

一个linner示例

_JAIL=""
[  -z "$_JAIL" ] && echo "No" || echo "Yes"

Click here 有关更多示例。