私はこのようなものを書きたいです:
if [[ ( releases["token"] & $MASK ) -eq 1 ]]; then
しかし、次のようなエラーが発生します。
予期しないトークン`&'、条件付き二項演算子が必要です
ifステートメントでビット演算子を使用するにはどうすればよいですか?
私はこのようなものを書きたいです:
if [[ ( releases["token"] & $MASK ) -eq 1 ]]; then
しかし、次のようなエラーが発生します。
予期しないトークン`&'、条件付き二項演算子が必要です
ifステートメントでビット演算子を使用するにはどうすればよいですか?
@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 が期待されますが4
、5
両方とも 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 & 5
vsの奇妙なケース4 & 5
==1
vsと、、および">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
どのオプションが設定されているか (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
動作する可能性はありますが、すべての組み合わせを構築する必要があるため、あまり良い方法ではありません!
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
if ステートメントで:
if (((releases["token"] & $MASK) == 1)); then
echo YES
else
echo NO
fi