Подтвердить что ты не робот

Как использовать побитовые операторы в операторах if?

Я хочу написать что-то вроде этого:

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

но я получаю сообщение об ошибке:

неожиданный токен `& ', ожидаемый условный двоичный оператор

Как использовать побитовые операторы в операторах if?

4b9b3361

Ответ 1

Вы можете использовать Arithmetic Expansion:

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

Он напечатает YES

Ответ 2

Логика против синтаксиса

.... или: "Что еще можно сказать?"

На первый взгляд, как указано в @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

Ответ 3

В выражении if:

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