【问题标题】:Portable way to check emptyness of a shell variable [duplicate]检查shell变量是否为空的便携式方法[重复]
【发布时间】:2013-11-16 00:42:23
【问题描述】:

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

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

对于反向,当变量不为空/未定义时做一些事情:

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

虽然这些适用于我现在编写的脚本,我想知道一种方法,它可以在任何合理兼容的类似 sh 的 shell 中工作,甚至在比具有 GNU 用户空间和 bashdash 的 Linux PC 更晦涩的环境中。

我正在寻找在不同 shell 环境中经验丰富并且知道不同做事方式的陷阱的人的“专家答案”,而不是像“应该有效”这样的意见。

【问题讨论】:

  • 你的方式就是的方式。您拥有的示例都遵循 POSIX 标准,并被dash(以及bashzsh)接受。只要你记得总是引用你的变量(就像你所做的那样)你是绝对安全的。可能除了一些非常老的 shell。有关这些奇怪案例的详细信息,请参阅下面的答案。
  • @zrajim - 这种方式不测试变量 - 它测试变量的扩展。
  • 你是对的,它测试了变量的扩展。如果变量为空/未定义,它将扩展为空字符串,因此我们只需要测试它以查看它是空的还是未定义的。

标签: bash sh dash-shell


【解决方案1】:

重要的是要记住,空的 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 的参数扩展来处理此类情况,在某些情况下甚至可以用一块石头杀死两只鸟(参见第一个示例)。这绝对应该是便携式的。以下是来自 open group 的 POSIX shell 指南的不完整复制/粘贴:http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02

${parameter:-word}

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

${parameter:=word}

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

${parameter:?[word]}

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

${parameter:+word}

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

示例:

${parameter:-word}

  • 在此示例中,仅当 xnullunset 时才会执行 ls。 ($( ls) 命令替换符号在命令替换中解释。) ${x:-$(ls)}

${parameter:=word}

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

编辑:

我刚刚注意到 Ashish 的单线,因为粘贴它感觉有点便宜,我想做点什么。所以这里有一个 POSIX 参数扩展 set-test one-liner:

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

嗯,我猜还是很便宜。

EDIT2:

所以我刚刚注意到:"...虽然这些适用于我现在编写的脚本,但我想知道一种方法,它可以在任何合理兼容的类似 sh 的 shell 中工作,甚至在某些比具有 GNU 用户空间和 bash 或 dash 的 Linux PC 更晦涩的环境......"

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

这有点漂亮,因为您只有在用户没有定义变量时才将变量设置为默认条件,而无需任何额外的工作来检查它们是否确实如此。当然还有很多其他用途,我只是不够聪明,无法按我应该的方式使用它们。

_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

【讨论】:

  • 关于参数扩展的很多很好的信息,您甚至没有尝试回答实际问题,是吗?
  • @zrajm - 你可能有道理。这是我的第一个答案之一——我应该再读一遍。但是${v:+:} echo if you see this - its empty
  • @zrajim - 我收回这一点 - 你没有意义。您的评论正上方是一个带有标题的格式化表格 set && !nullset && null!set,它详细描述了基于变量内容的每种扩展类型的行为 - 包括 empty 的一整列。你愿意再看看吗?
  • 是的。你说得对。里面有答案。我撤回了我的反对票。
【解决方案2】:

括号结构[...] 是POSIX 标准的一部分,几乎可以在任何地方使用。因此,要测试变量是否为空,问题中使用的 if 案例是完美的:

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

只要你记得:

总是引用变量

不带引号的变量会被分词,所以如果你使用[ -z $STRING ]$STRING 周围没有引号)并且$STRING 包含空格或为空,shell 将看到[ -z SEVERAL WORDS ] 或@ 987654328@ 这两个都是语法错误——即不是你想要的。

始终只使用一个等号

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

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

但请注意,您应该切勿[...] 中使用双等号 (==)! (这只适用于bash,或者当sh 是指向bash 的链接时——但如果在sh 链接到其他东西的不同机器上运行,它将不起作用。 ) 如果你想使用双等号,你还应该有双括号 [[...]](仅适用于 zshbash 等,但不适用于像 dash 这样的普通 POSIX shell)。

不要在[...] 中使用&&||

可能在某些 shell 中工作,但肯定不会在所有 shell 中工作。相反,对于 'and' 写 [...] && [...][... -a ...]。对于 'or' 写 [...] || [...][... -o ...]

古代的东西

有时,在非常古老的 shell 脚本中,您会看到如下内容,用于测试空变量。这仍然是完全有效的(但在我眼里看起来有点笨拙)而且我从来没有遇到过需要这个的 shell(虽然,诚然,我的经验在 Linux 之外是有限的——也许还有 BSD shell 仍然需要这个?)

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

句点的前置是为了避免括号构造 [...]$STRING 碰巧包含 -e-o[...] 使用的其他选项时混淆。通过前置句点,它变成了一个非选项。

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

【讨论】:

  • 嗯...但是如何判断它是否为空? [ test ] 的任何一种形式都不能以便携的方式告诉您变量是否为空——它们只能以便携的方式告诉您其扩展的结果。 empty != unset
  • 天啊!你是绝对正确的!我已经改写了我的答案,并附上了几个例子,说明如何按照问题的要求去做。
  • 几乎...问题是 - $STRING 不一定是 empty - 它可能是 要么 未设置 或 i> 空的。除非您定义条件扩展,否则您无法说出一种或另一种方式 - 不是没有set -u 和未引用的扩展,也就是说。喜欢:[ -z "${STRING+1}" ] 会告诉你$STRING 是否未设置,然后[ -z "${STRING:+1}" ] 会告诉你它是否为空。或者可能是[ -n "${STRING+1}" ] && [ ${#STRING} -eq 0 ] 之类的。例如,在与空的 readonlies 交互时,这可能会产生很大的不同。
  • 存在呢?
【解决方案3】:

便携和规范是两个不同的东西。

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

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

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

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

    • 测试括号 [...] 也是 POSIX 标准的一部分,并且更具可读性 - 坚持下去。 但是请注意,双括号 [[...]] 应该 不要在你追求兼容性时使用——这种结构特定于更复杂的 shell(zshbash 等)。
    • 任何使用 POSIX shell 的东西-not- 完全可以使用 任何合理兼容的类似 sh 的 shell。 POSIX 是一个新人,并不是在最初的sh 之前创建的。人们在尝试推广它之前应该知道他们的标准。
    • “Newcomer”对于 1992 年推出的东西可能有点过分。POSIX sh 已经过了投票年龄,即使它的 pappy Bourne 已经有几十年的历史了。
    【解决方案5】:

    除非您在测试变量中运行大代码,否则您不必进入 if else 阶梯

    一个线性例子

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

    Click here 更多示例。

    【讨论】:

    • 问题是关于进行测试本身的便携方式。您的代码本质上与相关代码一样可移植或不可移植。
    猜你喜欢
    • 2013-09-25
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 2013-08-08
    • 1970-01-01
    • 1970-01-01
    • 2015-09-06
    • 1970-01-01
    相关资源
    最近更新 更多