反转布尔变量


26

我想尝试简单的脚本

flag=false
while !$flag
do
   read x
   if [ "$x" -eq "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

但是,当我运行它时,如果键入true,我会看到x="true"flag="true",但是循环并没有结束。脚本有什么问题?如何正确地将布尔变量取反?



谢谢!我现在
知道

Answers:


21

您的脚本中有两个错误。首先,您需要在!和之间留一个空格$flag,否则shell将寻找一个名为的命令!$flag。第二个错误是-eq用于整数比较,但是您正在字符串上使用它。根据您的外壳,您可能会看到错误消息,并且由于条件[ "$x" -eq "true" ]不能为真,循环将永远继续,或者如果您输入任何字符串(包括false),则每个非整数值都将被视为0,并且循环将退出不同于0的数字。

虽然! $flag正确,但是将字符串视为命令是一个坏主意。可以,但是对脚本中的更改非常敏感,因为您需要确保该值$flag只能是trueor false。最好在这里使用字符串比较,例如下面的测试。

flag=false
while [ "$flag" != "true" ]
do
   read x
   if [ "$x" = "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

表达所追求的逻辑可能是更好的方法。例如,您可以进行无限循环并在检测到终止条件时将其中断。

while true; do
  read -r x
  if [ "$x" = "true" ]; then break; fi
  echo "$x: false"
done

使用[内置的ksh[ x -eq y ]如果x算术表达式解析为与y算术表达式相同的数字,则返回true ,例如,x=2 true=1+1 ksh -c '[ x -eq true ] && echo yes'将输出yes。
斯特凡·查泽拉斯

10

虽然Bash中没有布尔变量,但使用算术评估对其进行仿真非常容易。

flag= # False
flag=0 # False
flag=1 # True (actually any integer number != 0 will do, but see remark below about toggling)
flag="some string" # Maybe False (make sure that the string isn't interpreted as a number)

if ((flag)) # Test for True
then
  : # do something
fi

if ! ((flag)) # Test for False
then
  : # do something
fi

flag=$((1-flag)) # Toggle flag (only works when flag is either empty or unset, 0, 1, or a string which doesn't represent a number)

这也适用于ksh。如果它在所有POSIX兼容的shell中都可以使用,但我没有感到惊讶,但是还没有检查标准。


1
是否! ! ((val))可以像使用C或C ++一样在Bash中工作?例如,如果val为0 ,则在之后仍为0 ! !。如果val为1,则之后为1 ! !。如果val为8,则在之后将其拉低至1 ! !。还是我需要问另一个问题?

1
-1 | 在POSIX sh中,未定义独立((..));请删除对它的引用
LinuxSecurityFreak

Vlastimil的问题具体是关于bash的-不是posix shell。当问题与该主题无关时,为什么要投反对票?
尼克·布尔

6

如果您完全可以确定该变量将包含0或1,则可以使用按位XOR等于运算符在两个值之间切换:

$ foo=0
$ echo $foo
0
$ ((foo ^= 1))
$ echo $foo
1
$ ((foo ^= 1))
$echo $foo
0

2

这对我有用:

flag=false
while [[ "$flag" == "false" ]]
do
   read x
   if [[ "$x" == "true" ]]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

但是,实际上,您应该执行以下操作while

while ! $flag

0

Shell中没有布尔变量的概念。
shell变量只能是text(一个字符串),并且,在某些情况下,该文本可以被解释为一个整数(10xa010等)。

因此,a flag=true根本不暗示外壳的真实性或错误性。

可以执行的操作是字符串比较[ "$flag" == "true" ]或在某些命令中使用变量内容并检查其结果,例如执行true(因为同时存在true一个被调用的可执行文件和一个被调用的可执行文件false)作为命令,并检查该命令的退出代码是否为零(成功)。

$flag; if [ "$?" -eq 0 ]; then ... fi

或更短:

if "$flag"; then ... fi

如果将变量的内容用作!命令,则如果两个(! cmd)之间存在空格,则可以使用a 取反命令的退出状态,如下所示:

if ! "$flag"; then ... fi

该脚本应更改为:

flag=false
while ! "$flag" 
do
   read x
   if [ "$x" == "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

整数

使用数值和算术扩展

在这种情况下,$((0))is 1的退出代码和$((1))is 的退出代码0
在bash,ksh和zsh中,该算术可以在a内执行((..))(请注意,$缺少开始部分)。

flag=0; if ((flag)); then ... fi

此代码的可移植版本更加复杂:

flag=0; if [ "$((flag))" -eq 0 ]; then ... fi      # test for a number
flag=0; if [ "$((flag))"  == 0 ]; then ... fi      # test for the string "0"

在bash / ksh / zsh中,您可以执行以下操作:

flag=0    
while ((!flag)) 
do
   read x
   [ "$x" == "true" ] && flag=1
   echo "${x} : ${flag}"
done

或者

您可以将“反转布尔变量”(假设它包含数字值)为:

((flag=!flag))

这将改变的值flag要么01


注意:在将代码作为问题发布之前,请检查https://www.shellcheck.net/中的错误,这足以发现问题。


0

until的缩写while !(与Bourne until相反!),因此您可以执行以下操作:

flag=false
until "$flag"
do
   IFS= read -r x
   if [ "$x" = true ]
   then
     flag=true
   fi
   printf '%s\n' "$x : $flag"
done

哪个应该和:

flag=false
while ! "$flag"
do
   IFS= read -r x
   if [ "$x" = true ]
   then
     flag=true
   fi
   printf '%s\n' "$x : $flag"
done

您也可以将其编写为:

until
   IFS= read -r x
   printf '%s\n' "$x"
   [ "$x" = true ]
do
   continue
done

until/ while和之间的部分do不必是单个命令。

!是POSIX Shell语法中的关键字。它需要定界,令牌与后面的令牌分开,并且是管道之前的第一个令牌(! foo | bar否定管道,foo | ! bar尽管某些外壳程序接受它为扩展名,但它还是无效的)。

!true被解释为!true不太可能存在的命令。! true应该管用。!(true)应该在符合POSIX的炮弹工作作为!作为它的后面是分隔的(道理,但在实践中它并不在工作ksh/ bash -O extglob在那里与冲突!(pattern)扩展水珠运营商也不zsh的在那里它与冲突(除SH仿真)glob(qualifiers)bareglobqualglob(group)没有。


-1
[ ${FOO:-0} == 0 ] && FOO=1 || FOO=0

甚至:

[ ${FOO:-false} == false ] && FOO=true || FOO=false

应该做的工作

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.