【问题标题】:exclamation mark to test variable is true or not in bash shell用于测试变量的感叹号在 bash shell 中是否为真
【发布时间】:2017-05-03 10:35:45
【问题描述】:

我知道在 shell 中感叹号可以反转条件的结果。这里我想用它来测试变量是真还是假。

#! /bin/bash
bat=false
if [ ! $bar ]; then
    echo 'bar is false'
else
   echo 'bar is true'
fi

我期待“酒吧是假的”。然而结果却恰恰相反。然后我使用“$bar”==“false”。没错。

那么感叹号的使用技巧是什么。当我们测试文件时,它只会反转结果吗?

【问题讨论】:

  • Shell 没有布尔值,只有字符串变量。 truefalse 都只是字符串,与 kitten 等任何其他词没有本质上的不同。

标签: shell


【解决方案1】:

有关[test 实用程序的便携使用的详细信息,请参阅test 的POSIX 规范。

如果你使用[ ! $bar ],你就是在玩火。

  • 如果$bar为空字符串,则退化为单参数形式的test,即参数为!,并成功退出,因为!不是空字符串。

  • 如果$bar不是一个空字符串,那么参数是!$bar的值,它确定非空字符串是非空的,然后反转条件,所以它以失败状态退出。

您通常应该编写[ ! "$bar" ],如果$bar 为空,它将正常工作。您可以(可以说应该)使用显式测试:

  • [ -z "$bar" ] 测试空(零长度)字符串,并且
  • [ -n "$bar" ] 测试非空字符串

但引号是强制性的,以避免混淆。 non-empty 和 zero-length 是助记词。

$ bar=
$ [ ! $bar ] || echo Test failed
$ bar=xyz
$ [ ! $bar ] || echo Test failed
Test failed
$ [ ! "$bar" ] || echo Test failed
Test failed
$ bar=
$ [ ! "$bar" ] || echo Test failed
$ [ -n "$bar" ] || echo Test failed
Test failed
$ [ -z "$bar" ] || echo Test failed
$ 

有些人更喜欢 Bash (Korn shell, ...) 运算符 [[,它的行为不同。您可以试验此代码主题的变体:

set -x
bar=
[ ! $bar ] || echo Test failed
bar=xyz
[ ! $bar ] || echo Test failed
[ ! "$bar" ] || echo Test failed
bar=
[ ! "$bar" ] || echo Test failed
[ -n "$bar" ] || echo Test failed
[ -z "$bar" ] || echo Test failed
bar=xyz
[ -n "$bar" ] || echo Test failed
[ -z "$bar" ] || echo Test failed

bar=
[[ ! $bar ]] || echo Test failed
bar=xyz
[[ ! $bar ]] || echo Test failed
[[ ! "$bar" ]] || echo Test failed
bar=
[[ ! "$bar" ]] || echo Test failed
[[ -n "$bar" ]] || echo Test failed
[[ -z "$bar" ]] || echo Test failed
bar=xyz
[[ -n "$bar" ]] || echo Test failed
[[ -z "$bar" ]] || echo Test failed
bar=
[[ -n $bar ]] || echo Test failed
[[ -z $bar ]] || echo Test failed
bar=xyz
[[ -n $bar ]] || echo Test failed
[[ -z $bar ]] || echo Test failed
set +x  # Unnecessary if you save this in a script file and don't dot (source) it

它将(可能)帮助您了解正在发生的事情。不过,输出非常冗长。

【讨论】:

    【解决方案2】:

    tl;dr

    [ ! $bar ]$bar 视为字符串,并且任何非空 字符串都被视为“真” - 包括文字false ;换句话说:[ ! 'true' ] && [ ! 'false' ] both 评估为“false”,因为在这两种情况下操作数都是非空字符串,! 否定结果。

    因此,必须使用字符串比较:

    bar=false
    if [ ! "$bar" = 'true' ]; then  # same as: [ "$bar" != 'true' ]
        echo 'bar is false'
    else
        echo 'bar is true'
    fi
    

    在 Bash 中,您还可以使用 [[ ! $bar == 'true' ]][[ $bar != 'true' ]];除非你需要保持 POSIX 兼容,I suggest using [[ ... ]]


    [ ... ][[ ... ]](特定于Bash)的上下文中,默认情况下变量值是字符串,并且通常是POSIX-像 shell没有明确的布尔数据类型

    一元运算符!将其操作数隐式解释为布尔值,字符串在布尔上下文中解释如下:

    • 只有一个字符串被认为是“false”(退出代码1(!))
    • 任何非空字符串 - 包括文字 false - 被视为“真”(退出代码0( !))

    因此,! $bar 的计算结果为“假”,因为 $bar - 包含非空字符串 'false' - 计算结果为“真”,而 ! 将其反转。


    !也可以用在条件之外直接否定命令的成功状态(包括[)。
    由于falsetrue 也以命令名 的形式存在(通常作为shell 内置命令),您可以 执行以下操作,但请注意它只能按预期工作变量值可以是文字 falsetrue

    bar=false
    if ! "$bar"; then
       echo 'bar is false'
    else
       echo 'bar is true'
    fi
    

    背景资料

    类似 POSIX 的 shell 只有 2 种基本(标量)数据类型:

    • 字符串(默认):

      • [ ... ][[ ... ]] 条件句中,运算符=(Bash 中的==,,,>,>=` 执行字符串 em> 比较。
    • 整数:

      • [ ... ][[ ... ]] 条件中,必须使用不同的算术 运算符(-eqltlegtge)进行数值比较

      • 1234563 .
        在 Bash 中,您还可以将(( ... )) 用作算术条件

    注意:根据 POSIX,您不能将变量键入为整数(因为没有办法声明 shell 变量),所以它只是隐式 在使用算术运算符的比较中/在算术上下文中视为一个
    然而,在 Bash 中,您可以使用declare -i / local -i声明整数变量,但在条件/算术上下文中,它仍然是运算符/算术上下文的选择,它决定了值是否被视为字符串或整数。


    Shell 中的布尔值隐式表示为退出代码,在通常的布尔到整数映射的反转中:

    • “true”映射到退出代码0(!)
    • “false”表示为退出代码1(!) 或任何其他非零退出代码。

    【讨论】:

    • 非常感谢。现在我的困惑解决了。另一个快速的问题是,是否需要在 $bar 周围加双引号。我刚刚尝试了 if [ $bar ],它也按预期工作。
    • @nathan:简而言之:为了安全起见,双引号。在某些情况下,您不必严格遵守,例如这里,但规则不容易记住。每当您希望按原样使用变量值时,双引号是安全的。要了解更多信息,请参阅this question
    【解决方案3】:

    if 测试命令的结果,不能直接测试变量的值。在这种情况下,您将它与test/[ 命令结合使用,这是一个普通命令,可以在任何地方使用。

    [ -f ./foo.txt ] && echo "foo.txt exists"
    

    或等效

    test -f ./foo.txt && echo "foo.txt exists"
    

    两者都测试路径./foo.txt是否存在文件。

    根据您所说的“true”,您可以使用test[ shell 内置函数来确定一个变量是否等于另一个,以及用于测试目录或文件是否存在等其他一些功能。

    您可以使用if [ -z "$variable" ]; then ...; fi 来检查变量是否为空字符串。

    如果你想检查变量是否被设置,甚至是一个空值,你可以使用参数扩展[ -z "${var+x}" ]。当var 未定义时,它只会计算为true(意味着退出状态为零)。

    如果要将变量与固定字符串进行比较,可以使用 [ "$var" = "some-string" ]case,例如

    case "$var" in
       some-string)
         dostuff
       ;;
    esac
    

    请注意,您可以使用! 否定test/[ 的结果。

    if ! [ -z "$var" ]; then
        do-something
    fi
    

    否定条件。

    但是,! 作为test/[ 的参数也是有效的

    if [ ! -z "$var" ]; then
        do-something
    fi
    

    意思是一样的。

    【讨论】:

    • 感谢您的详细解释。我有一个关于“作为测试或 [.] 的论据没有意义的问题。”我确实看到“如果 [!-f 常规文件];那么”工作。是因为 -f 运算符吗?
    • @nathan: ![ … ] / [[ … ]] / 通常作为 test 操作数完全有效。你只需要了解它的操作数是如何被评估的:$bar 没有按照你期望的方式评估:它是一个 string,并且因为它是一个 nonempty 字符串 -不管它的内容 - 它被认为是“真实的”
    • @nathan,我错了。 !test 命令的有效参数。当且仅当"$somestring" 扩展为非空字符串时,test "$somestring" 将退出 0。 test ! "$something" 反转了这一点。话虽这么说,更典型的是使用test -z "$something"test -n "$something" 来测试空性,因为这可以处理$something 是像-z 这样的奇怪值的边缘情况。
    • @GregoryNisbet 非常感谢。
    猜你喜欢
    • 1970-01-01
    • 2017-07-26
    • 2015-03-09
    • 2019-09-16
    • 2017-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-26
    相关资源
    最近更新 更多