Я хочу написать что-то вроде этого:
if [[ ( releases["token"] & $MASK ) -eq 1 ]]; then
но я получаю сообщение об ошибке:
неожиданный токен `& ', ожидаемый условный двоичный оператор
Как использовать побитовые операторы в операторах if?
Я хочу написать что-то вроде этого:
if [[ ( releases["token"] & $MASK ) -eq 1 ]]; then
но я получаю сообщение об ошибке:
неожиданный токен `& ', ожидаемый условный двоичный оператор
Как использовать побитовые операторы в операторах if?
Вы можете использовать Arithmetic Expansion
:
(((5&3)==1)) && echo YES || echo NO
Он напечатает YES
На первый взгляд, как указано в @chepner comment, вопрос может быть только синтаксисом, который вызывает ошибку компиляции (unexpected token '&', conditional binary operator expected
), Фактически, @kev answer указывает, что используя арифметическое расширение, которое применяется к выражению If
в ответе @Gustavo.
Однако вопрос "как использовать побитовые операторы в операторах 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
будет печатать НЕТ, хотя в этом случае можно ожидать и ДА, так как 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
Мы могли бы, например, установить выбор этих опций в и вернуть объединенный код в качестве возвращаемого значения. Или другой way round: передать выбор параметров в виде одного числового параметра. Независимо от вашего варианта использования, параметры будут суммированы вместе:# 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
.- командные списки завершаются
;;&
, чтобы продолжить оценку с помощью следующего теста опций. В случае вы хотите отказаться от дальнейшей обработки списка, установите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