16

私はこのようなものを書きたいです:

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

しかし、次のようなエラーが発生します。

予期しないトークン`&'、条件付き二項演算子が必要です

ifステートメントでビット演算子を使用するにはどうすればよいですか?

4

3 に答える 3

27

使用できますArithmetic Expansion

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

YESと出力されます

于 2013-01-14T12:51:33.727 に答える
27

ロジックと構文

.... または: 「これ以上言うことはありますか?」

@chepner のコメントで指摘されているように、一見したところ、質問は構文の 1 つにすぎず、コンパイル エラー ( unexpected token '&', conditional binary operator expected) が発生する可能性があります。実際、@kev の回答は、 @Gustavo の回答のステートメントに 適用される算術展開を使用して対処しています。If

ただし、 「 ifステートメントでビット演算子を使用する方法という質問は、より一般的な方法で定式化されており、ビット演算子を使用してチェックする方法についての回答を求めています$MASK(「次の場合に構文エラーを回避する方法」の問題ではありませんでしたバイナリ比較を使用する」)。

実際、サンプルコードは

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

解決策を見つけるための進行中の作業であり、明示的に「何かのような...」とマークされています。今@kevの任意の仮定

 releases["token"]=3
 MASK=5

そもそもの使い方にも論理的な誤解があるという事実を隠しているかもしれません-eq 1。したがって、@kevの回答は選択した値で機能しますが、たとえば4行の入力の結果は

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

NOを出力しますが、その場合も YES が期待されますが45両方とも 3 番目のビットが設定されています。

したがって、この回答は、質問の例のこの論理エラーに対処し、質問の見出しに対する一般的な回答を試みます。


まず最初に!

... または: 「ビット単位表現の背景」

ビットごとの比較を理解するには、数値をビット表現で視覚化すると便利です (次の例では上から下にリストされています)。

|-----------------------------------------------------------|
|         |     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       |
|===========================================================|

これは、次のスニペットによって作成されます。

( # 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 ;
)


3 & 5vsの奇妙なケース4 & 5

... または: " ==1vsと、、および">0の違い&|^

言及された例の特殊なケースを確認すると、欠点がわかります。

|------------------ 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  |
|=========================================================|


次のチェックには特に注意してください。

|---------|-----------------------------------------------|
|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  |
|---------|-----------------------------------------------|


上記の出力は、次のスニペットによって作成されます。

( # 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 ;
)


「こんにちは世界」

...または: 「ライズ アンド シャイン!」

では、どうすればよいのでしょうか。次のオプション セットがあるとします。

# 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


たとえば、関数でこれらのオプションの選択を設定し、組み合わせたコードを戻り値として返すことができます。またはその逆: オプションの選択を 1 つの数値パラメーターとして渡します。ユースケースが何であれ、オプションは合計されます:

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


どのオプションが設定されているか ( 145、 & 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

動作する可能性はありますが、すべての組み合わせを構築する必要があるため、あまり良い方法ではありません!


そして勝者は ...

... または: 「リアルCase

代わりに次のように使用できます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です。
  • command-list;;&、次のオプション テストで評価を続行するために、 で終了し ます。リストのさらなる処理を中止したい場合は、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

万歳!:-)


そして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 "# 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


「質問に答えない者は試験に合格した。」

... または: 「滞在」と彼は言った、「それはただのテストでした。」

(カフカ、1975: 181)

したがって、次のようにわずかに変更することで、質問に投稿されたソリューションを使用することは完全に可能です。

(
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


完全に実行中:

( # 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"

)


終わり

Exit 0
于 2016-03-11T04:37:55.493 に答える
4

if ステートメントで:

if (((releases["token"] & $MASK) == 1)); then 
  echo YES
else 
  echo NO
fi
于 2014-05-28T16:33:38.677 に答える