重击“如果 [假];”返回真而不是假——为啥?

Posted

技术标签:

【中文标题】重击“如果 [假];”返回真而不是假——为啥?【英文标题】:bash "if [ false ];" returns true instead of false -- why?重击“如果 [假];”返回真而不是假——为什么? 【发布时间】:2013-11-09 07:21:59 【问题描述】:

为什么下面会输出True

#!/bin/sh

if [ false ]; then
    echo "True"
else
    echo "False"
fi

这将始终输出True,即使条件似乎另有说明。如果我删除括号[] 那么它可以工作,但我不明白为什么。

【问题讨论】:

Here's 帮助您入门。 顺便说一句,以#!/bin/sh 开头的脚本不是 bash 脚本——它是 POSIX sh 脚本。即使系统上的 POSIX sh 解释器是由 bash 提供的,它也会关闭一堆扩展。如果您想编写 bash 脚本,请使用 #!/bin/bash 或其本地对应的等效项(#!/usr/bin/env bash 使用 PATH 中的第一个 bash 解释器)。 这也会影响[[ false ]] 【参考方案1】:

正如@tripleee 所说,这充其量只是切线。

不过,如果您来到这里寻找类似的东西(就像我一样),这是我的解决方案

必须处理用户可访问的配置文件,我使用此功能:

function isTrue() 
    if [[ "$@^^" =~ ^(TRUE|OUI|Y|O$|ON$|[1-9]) ]]; then return 0;fi
    return 1
    

可以这样使用

if isTrue "$whatever"; then..

您可以更改正则表达式中的“真值列表”,此示例中的“真值列表”与法语兼容,并将“Yeah, yup, on,1, Oui,y,true”等字符串视为“True”。

请注意,'^^' 不区分大小写

【讨论】:

这充其量只是切线。您应该引用"$@^^" 和参数"$whatever"【参考方案2】:

添加上下文以希望有助于在此主题上提供一些额外的清晰度。对于 BaSH 新手来说,真/假陈述的感觉相当奇怪。举以下简单的例子及其结果。

此语句将返回“true”:

foo=" "; if [ "$foo" ]; then echo "true"; else echo "false"; fi

但这会返回“false”:

foo=" "; if [ $foo ]; then echo "true"; else echo "false"; fi

你明白为什么吗?第一个示例有一个带引号的 "" 字符串。这导致 BaSH 按字面意思对待它。所以,从字面上看,空格不是空的。虽然在非字面意义上(上面的第二个示例),但 BaSH(作为 $foo 中的值)将空格视为“无”,因此它等同于 null(此处解释为“假”)。

这些语句都会返回一个“false”的文本字符串:

foo=; if [ $foo ]; then echo "true"; else echo "false"; fi
foo=; if [ "$foo" ]; then echo "true"; else echo "false"; fi
foo=""; if [ $foo ]; then echo "true"; else echo "false"; fi
foo=""; if [ "$foo" ]; then echo "true"; else echo "false"; fi

有趣的是,这种类型的条件总是会返回 true:

这些语句都将返回“真”的结果:

foo=""; if [ foo ]; then echo "true"; else echo "false"; fi

注意区别;条件中变量名前面的 $ 符号已被省略。在括号之间插入什么词并不重要。 BaSH 将始终将此语句视为正确,即使您使用的词以前从未与同一 shell 中的变量相关联。

if [ sooperduper ]; then echo "true"; else echo "false"; fi

同样,将其定义为未声明的变量可确保它始终返回 false:

if [ $sooperduper ]; then echo "true"; else echo "false"; fi

至于 BaSH 和写作一样:

sooperduper="";if [ $sooperduper ]; then echo "true"; else echo "false"; fi

还有一个提示....

括号与无括号

让事情变得更加混乱,IF/THEN 条件的这些变化都有效,但返回相反的结果。

这些返回错误:

if [ $foo ]; then echo "true"; else echo "false"; fi
if [ ! foo ]; then echo "true"; else echo "false"; fi

但是,这些将返回 true 的结果:

if $foo; then echo "true"; else echo "false"; fi
if [ foo ]; then echo "true"; else echo "false"; fi
if [ ! $foo ]; then echo "true"; else echo "false"; fi

当然,这会返回一个语法错误(以及“假”的结果):

if foo; then echo "true"; else echo "false"; fi

困惑了吗?一开始就将其牢记在心可能非常具有挑战性,尤其是如果您习惯于其他更高级别的编程语言。

【讨论】:

【参考方案3】:

Bash 的快速布尔入门

if 语句将命令作为参数&&|| 等也是如此)。命令的整数结果代码被解释为布尔值(0/null=true, 1/else=false)。

test 语句将运算符和操作数作为参数,并返回与if 格式相同的结果代码。 test 语句的别名是 [,通常与 if 一起使用以执行更复杂的比较。

truefalse 语句不执行任何操作并返回结果代码(分别为 0 和 1)。因此它们可以用作 Bash 中的布尔文字。但是如果你把这些语句放在一个它们被解释为字符串的地方,你就会遇到问题。在你的情况下:

if [ foo ]; then ... # "if the string 'foo' is non-empty, return true"
if foo; then ...     # "if the command foo succeeds, return true"

所以:

if [ true  ] ; then echo "This text will always appear." ; fi;
if [ false ] ; then echo "This text will always appear." ; fi;
if true      ; then echo "This text will always appear." ; fi;
if false     ; then echo "This text will never appear."  ; fi;

这类似于 echo '$foo'echo "$foo" 之类的操作。

使用test 语句时,结果取决于使用的运算符。

if [ "$foo" = "$bar" ]   # true if the string values of $foo and $bar are equal
if [ "$foo" -eq "$bar" ] # true if the integer values of $foo and $bar are equal
if [ -f "$foo" ]         # true if $foo is a file that exists (by path)
if [ "$foo" ]            # true if $foo evaluates to a non-empty string
if foo                   # true if foo, as a command/subroutine,
                         # evaluates to true/success (returns 0 or null)

简而言之,如果您只是想测试通过/失败(也称为“true”/“false”),然后将命令传递给您的 if&& 等。声明,不带括号。对于复杂的比较,请使用带有适当运算符的括号。

是的,我知道 Bash 中没有原生布尔类型,if[true 在技术上是“命令”而不是“语句” ";这只是一个非常基本的功能性解释。

【讨论】:

仅供参考,如果您想在代码中使用 1/0 作为 True/False,则此 if (( $x )); then echo "Hello"; fi 显示 x=1 的消息,但不显示 x=0x= 的消息(未定义) .【参考方案4】:

使用 true/false 可以消除一些括号混乱...

#! /bin/bash    
#  true_or_false.bash

[ "$(basename $0)" == "bash" ] && sourced=true || sourced=false

$sourced && echo "SOURCED"
$sourced || echo "CALLED"

# Just an alternate way:
! $sourced  &&  echo "CALLED " ||  echo "SOURCED"

$sourced && return || exit

【讨论】:

我发现这很有帮助,但在 ubuntu 18.04 中,$0 是“-bash”,并且 basename 命令引发了错误。将该测试更改为 [[ "$0" =~ "bash" ]] 使脚本对我有用。【参考方案5】:

我发现我可以通过运行以下代码来完成一些基本逻辑:

A=true
B=true
if ($A && $B); then
    C=true
else
    C=false
fi
echo $C

【讨论】:

一个可以,但这是一个非常糟糕的主意。 ( $A && $B ) 是一个令人惊讶的低效操作——您通过调用 fork() 生成一个子进程,然后对 $A 的内容进行字符串拆分以生成一个元素列表(在这种情况下大小为 1),并评估每个元素作为一个 glob(在这种情况下是一个对自身求值的元素),然后将结果作为一个命令运行(在这种情况下,一个内置命令)。 此外,如果您的 truefalse 字符串来自其他地方,那么它们实际上可能包含 truefalse 以外的内容,您可能会遇到意外行为直至并包括任意命令执行。 A=1; B=1if (( A && B )) 更安全, - 将它们视为数字 - 或[[ $A && $B ]],将空字符串视为 false 和一个非空字符串为真。 (请注意,我只使用上面的全大写变量名来遵循答案建立的约定——POSIX 实际上explicitly specifies 用于环境变量和shell 内置函数的全大写名称, 并保留小写名称供应用程序使用;为了避免与未来的操作系统环境发生冲突,特别是考虑到设置 shell 变量会覆盖任何类似命名的环境变量,最好在自己的脚本中遵守该约定。【参考方案6】:

您正在运行带有参数“false”的[(又名test)命令,而不是运行命令false。由于“false”是一个非空字符串,test 命令总是成功的。要实际运行该命令,请删除 [ 命令。

if false; then
   echo "True"
else
   echo "False"
fi

【讨论】:

false 是一个命令,甚至是一个字符串的想法对我来说似乎很奇怪,但我想它是可行的。谢谢。 bash 没有布尔数据类型,因此没有表示真假的关键字。 if 语句仅检查您给它的命令是成功还是失败。 test 命令接受一个表达式,如果表达式为真则成功;非空字符串是一个计算结果为真的表达式,就像在大多数其他编程语言中一样。 false 是一个总是失败的命令。 (以此类推,true 是一个总是成功的命令。) if [[ false ]]; 也返回 true。 if [[ 0 ]]; 也是如此。 if [[ /usr/bin/false ]]; 也不会跳过该块。 false 或 0 如何评估为非零?该死的,这令人沮丧。如果重要,OS X 10.8;重击 3. 不要将false 误认为是布尔常量或将0 误认为是整数文字;两者都只是普通的字符串。对于不以连字符开头的任何字符串 x[[ x ]] 等效于 [[ -n x ]]bash 在任何上下文中都没有 any 布尔常量。看起来像整数的字符串在 (( ... )) 内被视为整数,或者在 [[ ... ]] 内使用 -eq-lt 等运算符。 @tbc0,手头的问题不仅仅是阅读方式。如果变量是由不完全由您控制的代码设置的(或者可以通过不寻常的文件名或其他方式在外部进行操作),if $predicate 很容易被滥用以以if [[ $predicate ]] 无法执行的方式执行恶意代码。跨度>

以上是关于重击“如果 [假];”返回真而不是假——为啥?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在shell中0为真而假为1?

没有明确说明条件的“如果(假)”

正则表达式未按预期进行评估

这是否可以检查swift数组中的所有值是否为真而不是逐个循环?

如何检查后台作业是不是存在? (重击)

JavaScript 运算符