重击“如果 [假];”返回真而不是假——为啥?
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
一起使用以执行更复杂的比较。
true
和 false
语句不执行任何操作并返回结果代码(分别为 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=0
或 x=
的消息(未定义) .【参考方案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(在这种情况下是一个对自身求值的元素),然后将结果作为一个命令运行(在这种情况下,一个内置命令)。
此外,如果您的 true
和 false
字符串来自其他地方,那么它们实际上可能包含 true
或 false
以外的内容,您可能会遇到意外行为直至并包括任意命令执行。
写A=1; B=1
和if (( 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 ]]
无法执行的方式执行恶意代码。跨度>
以上是关于重击“如果 [假];”返回真而不是假——为啥?的主要内容,如果未能解决你的问题,请参考以下文章