bash 如何在 if 语句中使用按位运算符?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14318451/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:11:11  来源:igfitidea点击:

How to use bitwise operators in if statements?

bashshellbit-manipulation

提问by EpsilonVector

I want to write something like this:

我想写这样的东西:

if [[ ( releases["token"] & $MASK ) -eq 1 ]]; then

but I get the error that:

但我得到的错误是:

unexpected token `&', conditional binary operator expected

意外标记“&”,预期条件二元运算符

How do I use bitwise operators in if statements?

如何在 if 语句中使用按位运算符?

回答by kev

You can use Arithmetic Expansion:

您可以使用Arithmetic Expansion

(((5&3)==1)) && echo YES || echo NO

It will print YES

它会打印YES

回答by Martin Rüegg

Logic vs Syntax

逻辑与语法

.... or: "What's more to say?"

.... 或:“还有什么好说的?”

At first sight, as pointed out in @chepner's comment, the question might only be one of syntax, which causes the compilation error (unexpected token '&', conditional binary operator expected). And in fact, @kev's answeraddresses that by using arithmetic expansion, which is applied to the Ifstatement in @Gustavo's answer.

乍一看,正如@chepner 的评论中指出的那样,问题可能只是语法之一,这会导致编译错误 ( unexpected token '&', conditional binary operator expected)。事实上,@kev 的回答通过使用算术扩展解决了这个问题,这适用If@Gustavo 的回答中的语句 。

However, the question "how to use bitwise operatorsin if statements"is formulated in a more general manner and begs for an answer on how to usebitwise operators to check against $MASK(It was not the question of "how to avoid a syntax error when using binary comparison").

但是,问题如何在 if 语句中使用按位运算符以更一般的方式表述,并要求就如何使用按位运算符进行检查提供答案$MASK(这不是“如何避免语法错误的问题”使用二进制比较”)。

In fact, it may be assumed that the example code

事实上,可以假设示例代码

if [[ ( releases["token"] & $MASK ) -eq 1 ]]; then
if [[ ( releases["token"] & $MASK ) -eq 1 ]]; then

was a work in progress of finding a solution and is explicitly marked as "something like ...". Now @kev's arbitrary assumption of

是一项正在寻找解决方案的工作,并被明确标记为“类似……”。现在@kev 的任意假设

 releases["token"]=3
 MASK=5

might hide the fact, that there is also a logical misunderstanding in using -eq 1in the first place. So while @kev's answer works with the chosen values, the result of the input of e.g. 4line in

可能会隐藏一个事实,即-eq 1首先使用时也存在逻辑上的误解。因此,虽然@kev 的答案适用于选定的值,但输入的结果例如4line in

 (((5&4)==1)) && echo YES || echo NO

would print NO, even though one would expect YES in that case as well, as 4and 5both have the 3rd bit set.

将打印NO,即使一个会是期望在该情况下,作为45两者有第3位集。

So thisanswer addresses this logicalerror in the questions example and attempts an general answer the the question's headline.

所以这个答案解决了问题示例中的这个逻辑错误,并尝试对问题的标题做出一般性的回答。



First things first!

第一件事!

... or: "The Background of Bitwise Representation"

...或:“按位表示的背景”

to understand bitwise comparison it is helpful to visualise numbers in their bit representation (listed top-down in the following example):

|-----------------------------------------------------------|
|         |     hexadecimal representation of the value     |
| bit val |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F |
|---------|---------------------- bit ----------------------|
|         |  shows if the given value has the given bit set |
|  0      |  -  x  -  x  -  x  -  x  -  x  -  x  -  x  -  x |
|  1      |  -  -  x  x  -  -  x  x  -  -  x  x  -  -  x  x |
|  2      |  -  -  -  -  x  x  x  x  -  -  -  -  x  x  x  x |
|  3      |  -  -  -  -  -  -  -  -  x  x  x  x  x  x  x  x |
|---------|---------------------- val ----------------------|
|     shows the value that the given bit adds to the number |
|  0   1  |  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1 |
|  1   2  |  0  0  2  2  0  0  2  2  0  0  2  2  0  0  2  2 |
|  2   4  |  0  0  0  0  4  4  4  4  0  0  0  0  4  4  4  4 |
|  3   8  |  0  0  0  0  0  0  0  0  8  8  8  8  8  8  8  8 |
|---------|-------------------------------------------------|
|sum:  15 |  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 |
|         |       decimal representation of the value       |
|===========================================================|

要理解按位比较,在位表示中可视化数字是有帮助的(在以下示例中自上而下列出):

|-----------------------------------------------------------|
|         |     hexadecimal representation of the value     |
| bit val |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F |
|---------|---------------------- bit ----------------------|
|         |  shows if the given value has the given bit set |
|  0      |  -  x  -  x  -  x  -  x  -  x  -  x  -  x  -  x |
|  1      |  -  -  x  x  -  -  x  x  -  -  x  x  -  -  x  x |
|  2      |  -  -  -  -  x  x  x  x  -  -  -  -  x  x  x  x |
|  3      |  -  -  -  -  -  -  -  -  x  x  x  x  x  x  x  x |
|---------|---------------------- val ----------------------|
|     shows the value that the given bit adds to the number |
|  0   1  |  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0  1 |
|  1   2  |  0  0  2  2  0  0  2  2  0  0  2  2  0  0  2  2 |
|  2   4  |  0  0  0  0  4  4  4  4  0  0  0  0  4  4  4  4 |
|  3   8  |  0  0  0  0  0  0  0  0  8  8  8  8  8  8  8  8 |
|---------|-------------------------------------------------|
|sum:  15 |  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 |
|         |       decimal representation of the value       |
|===========================================================|

which is created by the following snippet:

这是由以下代码段创建的:

( # start a subshell to encapsulate vars for easy copy-paste into the terminal
  # do not use this in a script!
echo ;
echo "|-----------------------------------------------------------|";
echo "|         |     hexadecimal representation of the value     |";
echo "| bit val |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F |";
echo "|---------|---------------------- bit ----------------------|";
echo "|         |  shows if the given value has the given bit set |";
mask=0;
for (( bit=0; bit<4; bit++)); do
    mask=$((1<<bit));
    echo -n "|  $bit      |"
    for ((x=0;x<16;x++)); do ((((x&mask)>0))&&echo -n '  x'||echo -n '  -'); done
    echo " |";
done ;
echo "|---------|---------------------- val ----------------------|";
echo "|     shows the value that the given bit adds to the number |";
mask=0;
for (( bit=0; bit<4; bit++)); do
    mask=$((1<<bit));
    echo -n "|  $bit   $mask  |"
    for ((x=0;x<16;x++)); do echo -n "  $((x&mask))"; done
    echo " |";
done ;
echo "|---------|-------------------------------------------------|";
echo "|sum:  15 |  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 |";
echo "|         |       decimal representation of the value       |";
echo "|===========================================================|";
echo ;
)



The curious case of 3 & 5vs 4 & 5

3 & 5vs的奇怪案例4 & 5

... or: "==1vs >0and the Differences of &, |, and ^"

...或者:“ ==1VS>0和的差异&|以及^

if we now check the special case of the examples mentioned, we'll see the shortcoming:

|------------------ value: 3 / mask: 5 -------------------|
|         | value | mask  |  and  |  or   |  xor  | xnor  |
|         |   3   |   5   | 3 & 5 | 3 | 5 | 3 ^ 5 |       |
| bit val |bit:val|bit:val|bit:val|bit:val|bit:val|bit:val|
|---------|-------|-------|-------|-------|-------|-------|
|  0   1  | 1   1 | 1   1 | 1   1 | 1   1 | 0   0 | 0   0 |
|  1   2  | 2   1 | 0   0 | 0   0 | 1   2 | 1   2 | 1   2 |
|  2   4  | 0   0 | 4   1 | 0   0 | 1   4 | 1   4 | 1   4 |
|  3   8  | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 |
|---------|-------|-------|-------|-------|-------|-------|
|sum:     |     3 |     5 | ==> 1 |     7 |     6 |     6 |
|---------|-----------------------------------------------|
|check:   |  3 & 5 == 1 ? YES     |                       |
|         |  3 & 5 >= 1 ? YES     | ==>  3 & 5 > 0 ? YES  |
|=========================================================|

|------------------ value: 4 / mask: 5 -------------------|
|         | value | mask  |  and  |  or   |  xor  | xnor  |
|         |   4   |   5   | 4 & 5 | 4 | 5 | 4 ^ 5 |       |
| bit val |bit:val|bit:val|bit:val|bit:val|bit:val|bit:val|
|---------|-------|-------|-------|-------|-------|-------|
|  0   1  | 0   0 | 1   1 | 0   0 | 1   1 | 1   1 | 1   1 |
|  1   2  | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 |
|  2   4  | 4   1 | 4   1 | 1   4 | 1   4 | 0   0 | 0   0 |
|  3   8  | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 |
|---------|-------|-------|-------|-------|-------|-------|
|sum:     |     4 |     5 | ==> 4 |     5 |     1 |     1 |
|---------|-----------------------------------------------|
|check:   |  4 & 5 == 1 ? NO      |                       |
|         |  4 & 5 >= 1 ? YES     | ==>  4 & 5 > 0 ? YES  |
|=========================================================|

如果我们现在检查提到的例子的特殊情况,我们会看到缺点:

|------------------ value: 3 / mask: 5 -------------------|
|         | value | mask  |  and  |  or   |  xor  | xnor  |
|         |   3   |   5   | 3 & 5 | 3 | 5 | 3 ^ 5 |       |
| bit val |bit:val|bit:val|bit:val|bit:val|bit:val|bit:val|
|---------|-------|-------|-------|-------|-------|-------|
|  0   1  | 1   1 | 1   1 | 1   1 | 1   1 | 0   0 | 0   0 |
|  1   2  | 2   1 | 0   0 | 0   0 | 1   2 | 1   2 | 1   2 |
|  2   4  | 0   0 | 4   1 | 0   0 | 1   4 | 1   4 | 1   4 |
|  3   8  | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 |
|---------|-------|-------|-------|-------|-------|-------|
|sum:     |     3 |     5 | ==> 1 |     7 |     6 |     6 |
|---------|-----------------------------------------------|
|check:   |  3 & 5 == 1 ? YES     |                       |
|         |  3 & 5 >= 1 ? YES     | ==>  3 & 5 > 0 ? YES  |
|=========================================================|

|------------------ value: 4 / mask: 5 -------------------|
|         | value | mask  |  and  |  or   |  xor  | xnor  |
|         |   4   |   5   | 4 & 5 | 4 | 5 | 4 ^ 5 |       |
| bit val |bit:val|bit:val|bit:val|bit:val|bit:val|bit:val|
|---------|-------|-------|-------|-------|-------|-------|
|  0   1  | 0   0 | 1   1 | 0   0 | 1   1 | 1   1 | 1   1 |
|  1   2  | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 |
|  2   4  | 4   1 | 4   1 | 1   4 | 1   4 | 0   0 | 0   0 |
|  3   8  | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 | 0   0 |
|---------|-------|-------|-------|-------|-------|-------|
|sum:     |     4 |     5 | ==> 4 |     5 |     1 |     1 |
|---------|-----------------------------------------------|
|check:   |  4 & 5 == 1 ? NO      |                       |
|         |  4 & 5 >= 1 ? YES     | ==>  4 & 5 > 0 ? YES  |
|=========================================================|


Please pay particular attention to the following checks:


请特别注意以下检查:

|---------|-----------------------------------------------|
|check:   |  3 & 5 == 1 ? YES     |                       |
|         |  3 & 5 >= 1 ? YES     | ==>  3 & 5 > 0 ? YES  |
---------|------------------------------------------------|
|check:   |  4 & 5 == 1 ? NO      |                       |
|         |  4 & 5 >= 1 ? YES     | ==>  4 & 5 > 0 ? YES  |
|---------|-----------------------------------------------|
|---------|-----------------------------------------------|
|check:   |  3 & 5 == 1 ? YES     |                       |
|         |  3 & 5 >= 1 ? YES     | ==>  3 & 5 > 0 ? YES  |
---------|------------------------------------------------|
|check:   |  4 & 5 == 1 ? NO      |                       |
|         |  4 & 5 >= 1 ? YES     | ==>  4 & 5 > 0 ? YES  |
|---------|-----------------------------------------------|


The output above is created by the following snippet:


上面的输出由以下代码段创建:

( # start a sub-shell to encapsulate vars for easy copy-paste into the terminal
  # do not use this in a script!
echo ;
for o in 3 4; do
echo "|------------------ value: $o / mask: 5 -------------------|";
echo "|         | value | mask  |  and  |  or   |  xor  | xnor  |";
echo "|         |   $o   |   5   | $o & 5 | $o | 5 | $o ^ 5 |       |";
echo "| bit val |bit:val|bit:val|bit:val|bit:val|bit:val|bit:val|";
echo "|---------|-------|-------|-------|-------|-------|-------|";
mask=0;
for (( bit=0; bit<4; bit++)); do
    mask=$((1<<bit));
    echo -n "|  $bit   $mask "
    echo -n " | $(($o&mask))   $((($o&mask)>0))"
    echo -n " | $((5&mask))   $(((5&mask)>0))"
    echo -n " | $(((($o&mask)&(5&mask))>0))   $((($o&mask)&(5&mask)))"
    echo -n " | $(((($o&mask)|(5&mask))>0))   $((($o&mask)|(5&mask)))"
    echo -n " | $(((($o&mask)^(5&mask))>0))   $((($o&mask)^(5&mask)))"
    echo -n " | $(((($o&mask)^(5&mask))>0))   $((($o&mask)^(5&mask)))"
    echo " |";
done ;
echo "|---------|-------|-------|-------|-------|-------|-------|";
echo -n "|sum:     |     $o |     5 |";
echo " ==> $(($o&5)) |     $(($o|5)) |     $(($o^5)) |     $(($o^5)) |";
echo "|---------|-----------------------------------------------|";
echo -n "|check:   |  $o & 5 == 1 ? $(((($o&5)==1))&&echo YES||echo "NO ")     ";
echo "|                       |";
echo -n "|         |  $o & 5 >= 1 ? $(((($o&5)>=1))&&echo YES||echo "NO ")     ";
echo "| ==>  $o & 5 > 0 ? $(((($o&5)>0))&&echo YES||echo "NO ")  |";
echo "|=========================================================|";
echo ;
done
echo ;
)



Hello World!

你好,世界!

... or: "Rise and Shine!"

……或:“崛起并闪耀!”

So how do we do it now?! Let's imagine, we have the following option set:

# option set:
option_1=1
option_2=2
option_3=4
option_4=8
option_5=16
option_6=32
option_7=64
option_8=128
option_9=256


We could for example set a selection of those options in a function and return the combined code as a return value. Or the other way round: pass a selection of options as one numeric parameter. Whatever your use case is, the options would be summed together:

# set options:
option = option_1
       + option_4
       + option_5
       + option_9


How do we best check, which options are set (1,4,5, & 9)? It depends of course again on your use case, but i kinda like the caseconstruct! Something like ...

case $option in
  0) echo "no option set!";;
  1) echo "option 1";;
  2) echo "option 2";;
  3) echo "option 1 and 2";;
  *) echo "...";;
esac

could work, but is not very nice, as we would have to construct every combination!

那我们现在怎么办?!让我们想象一下,我们有以下选项集:

# option set:
option_1=1
option_2=2
option_3=4
option_4=8
option_5=16
option_6=32
option_7=64
option_8=128
option_9=256


例如,我们可以在函数中设置这些选项的选择,并将组合代码作为返回值返回。或者反过来:将一组选项作为一个数字参数传递。无论您的用例是什么,这些选项都将汇总在一起:

# set options:
option = option_1
       + option_4
       + option_5
       + option_9


我们如何最好地检查设置了哪些选项(1, 4, 5, & 9)?这当然再次取决于您的用例,但我有点喜欢这种case构造!就像是 ...

case $option in
  0) echo "no option set!";;
  1) echo "option 1";;
  2) echo "option 2";;
  3) echo "option 1 and 2";;
  *) echo "...";;
esac

可以工作,但不是很好,因为我们必须构建每个组合!



And the winner is ...

最终获胜者是 ...

... or: "The Real Case"

......或:“真实的Case

We can use casein the following way instead ...

echo "check for options using >0:"
case 1 in
  $(( (option & option_1) >0 )) ) echo "- option_1 is set";;&
  $(( (option & option_2) >0 )) ) echo "- option_2 is set";;&
  $(( (option & option_3) >0 )) ) echo "- option_3 is set";;&
  $(( (option & option_4) >0 )) ) echo "- option_4 is set";;&
  $(( (option & option_5) >0 )) ) echo "- option_5 is set";;&
  $(( (option & option_6) >0 )) ) echo "- option_6 is set";;&
  $(( (option & option_7) >0 )) ) echo "- option_7 is set";;&
  $(( (option & option_8) >0 )) ) echo "- option_8 is set";;&
  $(( (option & option_9) >0 )) ) echo "- option_9 is set";;&
esac

Please notethat

  • the spaces within $(( (option & option_1) >0 )) )are completely optional and are added for readability. The last closing bracket )is for the caseconstruct.
  • the command-lists are terminated with ;;&, in order to continue evaluation with the next option test. In case you want to abord further processing of the list, set option=0or to your current option=option_X.

... we get the following result:

check for options using >0:
- option_1 is set
- option_4 is set
- option_5 is set
- option_9 is set

Hurray! :-)

我们可以通过case以下方式使用...

echo "check for options using >0:"
case 1 in
  $(( (option & option_1) >0 )) ) echo "- option_1 is set";;&
  $(( (option & option_2) >0 )) ) echo "- option_2 is set";;&
  $(( (option & option_3) >0 )) ) echo "- option_3 is set";;&
  $(( (option & option_4) >0 )) ) echo "- option_4 is set";;&
  $(( (option & option_5) >0 )) ) echo "- option_5 is set";;&
  $(( (option & option_6) >0 )) ) echo "- option_6 is set";;&
  $(( (option & option_7) >0 )) ) echo "- option_7 is set";;&
  $(( (option & option_8) >0 )) ) echo "- option_8 is set";;&
  $(( (option & option_9) >0 )) ) echo "- option_9 is set";;&
esac

请注意的是

  • 里面的空格$(( (option & option_1) >0 )) )是完全可选的,并且是为了可读性而添加的。最后一个右括号)用于case构造。
  • 命令列表s的终止 ;;&,以继续评估下一个选项测试。如果您想放弃对列表的进一步处理,请将option=0或设置为您当前的option=option_X.

...我们得到以下结果:

check for options using >0:
- option_1 is set
- option_4 is set
- option_5 is set
- option_9 is set

欢呼!:-)



And ifI was a rich man!

而且if我还是个有钱人!

echo "# to use it in an if statement:";
if (((option&option_5)>0)); then
  echo "- option_5 is set"    
else
  echo "- option_5 is NOT set"
fi

# to use it in an if statement:
- option_5 is set
echo "# to use it in an if statement:";
if (((option&option_5)>0)); then
  echo "- option_5 is set"    
else
  echo "- option_5 is NOT set"
fi

# to use it in an if statement:
- option_5 is set



But finally the poor man's condition:

但最后是穷人的状况:

echo "# or to use it just for conditional execution:";
(((option&option_6)>0)) \
&& echo "- option_6 is set" \
|| echo "- option_6 is NOT set"

# or to use it just for conditional execution:
- option_6 is NOT set
echo "# or to use it just for conditional execution:";
(((option&option_6)>0)) \
&& echo "- option_6 is set" \
|| echo "- option_6 is NOT set"

# or to use it just for conditional execution:
- option_6 is NOT set



"He who does not answer the questions has passed the test."

“不回答问题的人通过了测试。”

... or: 'Stay,' he said, 'that was only a test.'

……或者:“留下来,”他说,“那只是一个考验。”

(Kafka, 1975: 181)

(卡夫卡,1975:181)

So it would be perfectly possible to use the solution as posted in the question, by the slight change as follows:

(
declare -A releases=([token]=4)
declare -i MASK=5

if [[ $(( releases["token"] & $MASK )) -gt 0 ]]; then
  echo YES
else
  echo NO
fi
)

The changes are the following: - use $(())instead of ()to do the test, which will return the value of the bitwise comparison - use -gt 0instead of -eq 1

因此,完全可以使用问题中发布的解决方案,只需稍作更改,如下所示:

(
declare -A releases=([token]=4)
declare -i MASK=5

if [[ $(( releases["token"] & $MASK )) -gt 0 ]]; then
  echo YES
else
  echo NO
fi
)

更改如下: - 使用$(())代替进行()测试,这将返回按位比较的值 - 使用-gt 0代替-eq 1


altogether in action:


完全在行动:

( # start a sub-shell to encapsulate vars for easy copy-paste into the terminal
  # do not use this in a script!
echo ;

for ((i=0; i<9;i++)); do
    o="option_$((i+1))"
    declare -i $o=$((1<<$i))
    echo "$o = ${!o}"
done

echo ;
echo ;

echo "# set options:"
echo "option = option_1"
echo "       + option_4"
echo "       + option_5"
echo "       + option_9"

option=option_1+option_4+option_5+option_9

echo ;
echo ;

echo "check for options using >0:"
case 1 in
  $(( (option & option_1) >0 )) ) echo "- option_1 is set";;&
  $(( (option & option_2) >0 )) ) echo "- option_2 is set";;&
  $(( (option & option_3) >0 )) ) echo "- option_3 is set";;&
  $(( (option & option_4) >0 )) ) echo "- option_4 is set";;&
  $(( (option & option_5) >0 )) ) echo "- option_5 is set";;&
  $(( (option & option_6) >0 )) ) echo "- option_6 is set";;&
  $(( (option & option_7) >0 )) ) echo "- option_7 is set";;&
  $(( (option & option_8) >0 )) ) echo "- option_8 is set";;&
  $(( (option & option_9) >0 )) ) echo "- option_9 is set";;&
esac

echo ;
echo ;

echo "# to use it in an if statement:";
echo "  => if (((option&option_5)>0));";
if (((option&option_5)>0)); then
    echo "- option_5 is set"    
else
    echo "- option_5 is NOT set"
fi

echo ;
echo ;

declare -A releases=([token]=4)
declare -i MASK=5

echo "# Does 4 pass the mask 5 in the test construct?";
echo "  => if [[ $(( releases["token"] & $MASK )) -gt 0 ]];";
if [[ $(( releases["token"] & $MASK )) -gt 0 ]]; then
    echo YES
else
    echo NO
fi

echo ;
echo ;

echo "# or to use it just for conditional execution:";
echo "  => (((option&option_6)>0)) && do || dont";
(((option&option_6)>0)) \
&& echo "- option_6 is set" \
|| echo "- option_6 is NOT set"

)



The End

结束

Exit 0

回答by Gustavo

In if statement:

在 if 语句中:

if (((releases["token"] & $MASK) == 1)); then 
  echo YES
else 
  echo NO
fi