【问题标题】:Test for non-zero length string in Bash: [ -n "$var" ] or [ "$var" ]在 Bash 中测试非零长度字符串: [ -n "$var" ] 或 [ "$var" ]
【发布时间】:2011-04-21 14:37:55
【问题描述】:

我见过 Bash 脚本以两种不同的方式测试非零长度字符串。大多数脚本使用-n 选项:

#!/bin/bash
# With the -n option
if [ -n "$var" ]; then
  # Do something when var is non-zero length
fi

但实际上并不需要 -n 选项:

# Without the -n option
if [ "$var" ]; then
  # Do something when var is non-zero length
fi

哪种方法更好?

同样,这是测试零长度的更好方法:

if [ -z "$var" ]; then
  # Do something when var is zero-length
fi

if [ ! "$var" ]; then
  # Do something when var is zero-length
fi

【问题讨论】:

    标签: bash shell if-statement syntax scripting


    【解决方案1】:

    编辑:这是一个更完整的版本,显示了 [(又名test)和 [[ 之间的更多差异.

    下表显示了变量是否被引用,使用单括号还是双括号以及变量是否仅包含空格是影响使用带或不带-n/-z的测试是否适合检查的因素一个变量。

         | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b
         | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"
    -----+------------------------------------+------------------------------------
    unset| false false true  false true  true | false false false false true  true
    null | false false true  false true  true | false false false false true  true
    space| false true  true  true  true  false| true  true  true  true  false false
    zero | true  true  true  true  false false| true  true  true  true  false false
    digit| true  true  true  true  false false| true  true  true  true  false false
    char | true  true  true  true  false false| true  true  true  true  false false
    hyphn| true  true  true  true  false false| true  true  true  true  false false
    two  | -err- true  -err- true  -err- false| true  true  true  true  false false
    part | -err- true  -err- true  -err- false| true  true  true  true  false false
    Tstr | true  true  -err- true  -err- false| true  true  true  true  false false
    Fsym | false true  -err- true  -err- false| true  true  true  true  false false
    T=   | true  true  -err- true  -err- false| true  true  true  true  false false
    F=   | false true  -err- true  -err- false| true  true  true  true  false false
    T!=  | true  true  -err- true  -err- false| true  true  true  true  false false
    F!=  | false true  -err- true  -err- false| true  true  true  true  false false
    Teq  | true  true  -err- true  -err- false| true  true  true  true  false false
    Feq  | false true  -err- true  -err- false| true  true  true  true  false false
    Tne  | true  true  -err- true  -err- false| true  true  true  true  false false
    Fne  | false true  -err- true  -err- false| true  true  true  true  false false
    

    如果您想知道变量的长度是否为非零,请执行以下任一操作:

    • 在单括号中引用变量(第 2a 列)
    • 使用 -n 并在单括号中引用变量(第 4a 列)
    • 使用双括号,带或不带引号,带或不带-n(1b - 4b 列)

    请注意,在第 1a 列中,从标记为“二”的行开始,结果表明 [ 正在评估变量的 内容,就好像它们是条件表达式的一部分一样(结果匹配描述栏中的“T”或“F”暗示的断言)。当使用[[ 时(第 1b 列),变量内容被视为一个字符串并且不被计算。

    第 3a 列和第 5a 列中的错误是由于变量值包含空格且变量未加引号引起的。同样,如第 3b 和 5b 列所示,[[ 将变量的内容评估为字符串。

    相应地,对于零长度字符串的测试,第 6a、5b 和 6b 列显示了执行此操作的正确方法。另请注意,如果否定显示比使用相反操作更清晰的意图,则可以否定任何这些测试。例如:if ! [[ -n $var ]]

    如果您使用[,确保不会得到意外结果的关键是引用变量。用[[,没关系。

    被抑制的错误消息是“预期的一元运算符”或“预期的二元运算符”。

    这是生成上表的脚本。

    #!/bin/bash
    # by Dennis Williamson
    # 2010-10-06, revised 2010-11-10
    # for http://stackoverflow.com/q/3869072
    # designed to fit an 80 character terminal
    
    dw=5    # description column width
    w=6     # table column width
    
    t () { printf '%-*s' "$w" " true"; }
    f () { [[ $? == 1 ]] && printf '%-*s' "$w" " false" || printf '%-*s' "$w" " -err-"; }
    
    o=/dev/null
    
    echo '     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b'
    echo '     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"'
    echo '-----+------------------------------------+------------------------------------'
    
    while read -r d t
    do
        printf '%-*s|' "$dw" "$d"
    
        case $d in
            unset) unset t  ;;
            space) t=' '    ;;
        esac
    
        [ $t ]        2>$o  && t || f
        [ "$t" ]            && t || f
        [ -n $t ]     2>$o  && t || f
        [ -n "$t" ]         && t || f
        [ -z $t ]     2>$o  && t || f
        [ -z "$t" ]         && t || f
        echo -n "|"
        [[ $t ]]            && t || f
        [[ "$t" ]]          && t || f
        [[ -n $t ]]         && t || f
        [[ -n "$t" ]]       && t || f
        [[ -z $t ]]         && t || f
        [[ -z "$t" ]]       && t || f
        echo
    
    done <<'EOF'
    unset
    null
    space
    zero    0
    digit   1
    char    c
    hyphn   -z
    two     a b
    part    a -a
    Tstr    -n a
    Fsym    -h .
    T=      1 = 1
    F=      1 = 2
    T!=     1 != 2
    F!=     1 != 1
    Teq     1 -eq 1
    Feq     1 -eq 2
    Tne     1 -ne 2
    Fne     1 -ne 1
    EOF
    

    【讨论】:

    • 谢谢!我决定采用“在单括号中引用变量(第 2a 列)”IMO 的风格,-n 只会增加噪音并降低可读性。同样,对于零长度或未设置的测试,我将使用 [ ! "$var" ] 而不是 [ -z "$var" ]。
    • 所以您的["[-n"(OP 的第一个问题)的图表显示它们是完全等价的,对吧?
    • @hobs:是的,使用哪个取决于哪个更清楚。您还会注意到它们在使用双括号形式时的等价物,这在使用 Bash 时是首选。
    • 所以,总结一下您的出色表格,测试非空字符串的更清晰(“更好”)的方法是 [["
    【解决方案2】:

    就 Bash 而言,最好使用 more powerful [[

    一般情况

    if [[ $var ]]; then   # var is set and it is not empty
    if [[ ! $var ]]; then # var is not set or it is set to an empty string
    

    以上两个结构看起来干净且可读。在大多数情况下它们应该就足够了。

    请注意,我们不需要在[[ 中引用变量扩展,因为word splittingglobbing 没有危险。

    为了防止shellcheck[[ $var ]][[ ! $var ]] 的软投诉,我们可以使用-n 选项。

    罕见情况

    在我们不得不区分“被设置为空字符串”和“根本没有被设置”之间的罕见情况下,我们可以使用这些:

    if [[ ${var+x} ]]; then           # var is set but it could be empty
    if [[ ! ${var+x} ]]; then         # var is not set
    if [[ ${var+x} && ! $var ]]; then # var is set and is empty
    

    我们也可以使用-v 测试:

    if [[ -v var ]]; then             # var is set but it could be empty
    if [[ ! -v var ]]; then           # var is not set
    if [[ -v var && ! $var ]]; then   # var is set and is empty
    if [[ -v var && -z $var ]]; then  # var is set and is empty
    

    相关帖子和文档

    有很多与此主题相关的帖子。以下是一些:

    【讨论】:

    • 我认为使用[[ 不一定更好。这是个人喜好的问题。即使您引用的the link 也建议始终使用[]
    【解决方案3】:

    这里还有一些测试

    如果字符串不为空则为真:

    [ -n "$var" ]
    [[ -n $var ]]
    test -n "$var"
    [ "$var" ]
    [[ $var ]]
    (( ${#var} ))
    let ${#var}
    test "$var"
    

    如果字符串为空则为真:

    [ -z "$var" ]
    [[ -z $var ]]
    test -z "$var"
    ! [ "$var" ]
    ! [[ $var ]]
    ! (( ${#var} ))
    ! let ${#var}
    ! test "$var"
    

    【讨论】:

    • 这并不能回答 OP 关于什么是更好的方法的问题。
    【解决方案4】:

    正确答案如下:

    if [[ -n $var ]] ; then
      blah
    fi
    

    注意[[...]] 的使用,它会为您正确处理引用变量。

    【讨论】:

    • 为什么在 Bash 中并不真正需要 -n 时使用它?
    • @codeforester BASH (Bourne Again Shell) 是 Bourne Shell 的后代,因此它继承了 -n-z 测试运算符,但在其顶部添加了 ! 否定。此外,这在概念上与高级语言中的==!= 之间的区别相同,高级语言提供肯定和否定运算符只是为了扩展可能的语义范围。有时组装一个不需要依赖双重否定等的表达式要容易得多。
    【解决方案5】:

    评估空环境变量的另一种可能更透明的方法是使用...

      if [ "x$ENV_VARIABLE" != "x" ] ; then
          echo 'ENV_VARIABLE contains something'
      fi
    

    【讨论】:

    • 这是 Bourne shell 时代的老派。不要延续这个旧习惯。 bash 是比它的前辈更锋利的工具。
    • 如果您避免使用 POSIX 标准明确标记为过时的语法,您甚至不需要使用 pre-bash shell。 [ "$ENV_VARIABLE" != "" ] 可以在 每个 shell 上使用符合 POSIX 的 test 实现——不仅仅是 bash,还有 ash/dash/ksh/etc。
    • ...也就是说,不要使用-a-o 来组合测试,而是使用[ ... ] &amp;&amp; [ ... ][ ... ] || [ ... ] 以及唯一可以适用于使用的极端情况测试中任意变量的数据是明确封闭的。
    【解决方案6】:

    使用case/esac进行测试:

    case "$var" in
      "") echo "zero length";;
    esac
    

    【讨论】:

    • 不,请。这不是case 的用途。
    • case 在有两个以上备选方案时效果最佳。
    • case 不适合这种用途。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-15
    • 2017-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多