Инвертировать логическую переменную

26

Я хочу попробовать простой скрипт

flag=false
while !$flag
do
   read x
   if [ "$x" -eq "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

Но когда я запускаю его, если я набираю true, я увижу это x="true"и flag="true", но цикл не заканчивается. Что не так со сценарием? Как правильно инвертировать логическую переменную?

breedish
источник
Спасибо! я получил сейчас
breedish

Ответы:

21

В вашем скрипте есть две ошибки. Во-первых, вам нужен пробел между !и $flag, в противном случае оболочка ищет команду с именем !$flag. Вторая ошибка - -eqдля целочисленных сравнений, но вы используете ее в строке. В зависимости от вашей оболочки, либо вы увидите сообщение об ошибке, и цикл будет продолжаться вечно, потому что условие [ "$x" -eq "true" ]не может быть истинным, либо каждое нецелое значение будет рассматриваться как 0, и цикл завершится, если вы введете какую-либо строку (включая false) кроме числа, отличного от 0.

Хотя ! $flagэто правильно, плохая идея рассматривать строку как команду. Это будет работать, но это будет очень чувствительно к изменениям в вашем скрипте, так как вам нужно убедиться, что $flagэто никогда не может быть чем-либо, кроме trueили false. Было бы лучше использовать сравнение строк здесь, как в тесте ниже.

flag=false
while [ "$flag" != "true" ]
do
   read x
   if [ "$x" = "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

Вероятно, есть лучший способ выразить логику, которую вы ищете. Например, вы можете сделать бесконечный цикл и разорвать его, когда обнаружите условие завершения.

while true; do
  read -r x
  if [ "$x" = "true" ]; then break; fi
  echo "$x: false"
done
Жиль "ТАК - перестань быть злым"
источник
С [встроенной командой из ksh, [ x -eq y ]возвращает истину если xарифметических выражений решает в том же количестве, в yарифметическом выражении , так, например , x=2 true=1+1 ksh -c '[ x -eq true ] && echo yes'было бы выход да.
Стефан Шазелас
10

Хотя в Bash нет логических переменных, их очень легко эмулировать с помощью арифметической оценки.

flag= # False
flag=0 # False
flag=1 # True (actually any integer number != 0 will do, but see remark below about toggling)
flag="some string" # Maybe False (make sure that the string isn't interpreted as a number)

if ((flag)) # Test for True
then
  : # do something
fi

if ! ((flag)) # Test for False
then
  : # do something
fi

flag=$((1-flag)) # Toggle flag (only works when flag is either empty or unset, 0, 1, or a string which doesn't represent a number)

Это также работает в ksh. Я не удивлюсь, если он работает во всех POSIX-совместимых оболочках, но не проверил стандарт.

извед
источник
1
Работает ли ! ! ((val))в Bash, как в C или C ++? Например, если val0, оно остается 0 после ! !. Если val1, то остается 1 после ! !. Если значение valравно 8, оно уменьшается до 1 после ! !. Или мне нужно задать еще один вопрос?
1
-1 | В POSIX sh автономный ((..)) не определен; пожалуйста, удалите ссылку на него
LinuxSecurityFreak
Властимил вопрос конкретно про bash - не posix shell. Зачем голосовать, если вопрос не об этой теме?
Ник Булл,
6

Если вы можете быть абсолютно уверены, что переменная будет содержать 0 или 1, вы можете использовать побитовый оператор XOR-equal для переключения между двумя значениями:

$ foo=0
$ echo $foo
0
$ ((foo ^= 1))
$ echo $foo
1
$ ((foo ^= 1))
$echo $foo
0
user971217
источник
2

Это работает для меня:

flag=false
while [[ "$flag" == "false" ]]
do
   read x
   if [[ "$x" == "true" ]]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

Но, на самом деле, то, что вы должны сделать, это заменить вашу while:

while ! $flag
ДМИТРИЙ МАЛИКОВ
источник
0

В оболочке нет понятия булевой переменной.
Переменные оболочки могут быть только text(строка), и, в некоторых случаях, этот текст может быть истолкован как целое число ( 1, 0xa, 010и т.д.).

Следовательно, a не flag=trueподразумевает никакой правдивости или ложности для оболочки.

строка

То, что можно сделать, - это либо сравнение строк, [ "$flag" == "true" ]либо использование содержимого переменной в некоторой команде, и проверка ее последствий , например, выполнение true(потому что есть и вызываемый исполняемый файл, trueи вызываемый false) в качестве команды, и проверка, равен ли код выхода этой команды нулю. (успешно).

$flag; if [ "$?" -eq 0 ]; then ... fi

Или короче:

if "$flag"; then ... fi

Если содержимое переменной используется в качестве команды, то !можно использовать ее для отмены статуса завершения команды, если между двумя ( ! cmd) существует пробел , как в:

if ! "$flag"; then ... fi

Сценарий должен измениться на:

flag=false
while ! "$flag" 
do
   read x
   if [ "$x" == "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

целое число

Используйте числовые значения и арифметические расширения .

В этом случае код выхода $((0))is 1и код выхода $((1))is 0.
В bash, ksh и zsh арифметика может выполняться внутри a ((..))(обратите внимание, что запуск $отсутствует).

flag=0; if ((flag)); then ... fi

Портативная версия этого кода более запутанная:

flag=0; if [ "$((flag))" -eq 0 ]; then ... fi      # test for a number
flag=0; if [ "$((flag))"  == 0 ]; then ... fi      # test for the string "0"

В bash / ksh / zsh вы можете сделать:

flag=0    
while ((!flag)) 
do
   read x
   [ "$x" == "true" ] && flag=1
   echo "${x} : ${flag}"
done

альтернативно

Вы можете «инвертировать логическую переменную» (при условии, что она содержит числовое значение) как:

((flag=!flag))

Это изменит значение flagлибо на, 0либо 1.


Примечание . Пожалуйста, проверьте ошибки на https://www.shellcheck.net/, прежде чем публиковать свой код в виде вопроса. Этого достаточно, чтобы найти проблему.

Исаак
источник
0

untilэто сокращение от while !until, в отличие от !Борна), так что вы можете сделать:

flag=false
until "$flag"
do
   IFS= read -r x
   if [ "$x" = true ]
   then
     flag=true
   fi
   printf '%s\n' "$x : $flag"
done

Который должен быть таким же, как:

flag=false
while ! "$flag"
do
   IFS= read -r x
   if [ "$x" = true ]
   then
     flag=true
   fi
   printf '%s\n' "$x : $flag"
done

Вы также можете написать это как:

until
   IFS= read -r x
   printf '%s\n' "$x"
   [ "$x" = true ]
do
   continue
done

Часть между until/ whileи doне должна быть единой командой.

!является ключевым словом в синтаксисе оболочки POSIX. Он должен быть разделен, токен отделен от следующего и быть первым токеном, предшествующим конвейеру ( ! foo | barотрицает конвейер и foo | ! barявляется недействительным, хотя некоторые оболочки принимают его как расширение).

!trueинтерпретируется как !trueкоманда, которая вряд ли существует. ! trueдолжно сработать. !(true)должен работать в POSIX-совместимых оболочках, поскольку !он разделен, поскольку за ним следует (токен, но на практике он не работает в ksh/ bash -O extglobтам, где он конфликтует с !(pattern)оператором расширенного glob, ни с zsh (кроме эмуляции sh), где он конфликтует glob(qualifiers)с bareglobqualи glob(group)без.

Стефан Шазелас
источник
-1
[ ${FOO:-0} == 0 ] && FOO=1 || FOO=0

или даже:

[ ${FOO:-false} == false ] && FOO=true || FOO=false

должен делать работу

slaxor
источник