Інвертувати булева змінна


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", але цикл не закінчується. Що не так із сценарієм? Як я можу правильно інвертувати булеву змінну?



Дякую! я отримав це зараз
породілля

Відповіді:


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 ++? Наприклад, якщо valце 0, він залишається 0 після ! !. Якщо valце 1, він залишається 1 після ! !. Якщо valдорівнює 8, її знімають до 1 після ! !. Або мені потрібно задати інше питання?

1
-1 | У POSIX sh, окремий ((..)) не визначений; видаліть посилання на нього
LinuxSecurityFreak

Властиміл питання конкретно стосується bash - не posix shell. Чому вниз голосувати, коли питання не стосується цієї теми?
Нік Булл

6

Якщо ви можете бути абсолютно впевнені, що змінна буде містити або 0, або 1, ви можете використовувати оператор, рівний бітовому XOR, для перемикання між двома значеннями:

$ foo=0
$ echo $foo
0
$ ((foo ^= 1))
$ echo $foo
1
$ ((foo ^= 1))
$echo $foo
0

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і т.д.).

Тому flag=trueмається на увазі відсутність правдивості або неправдивості для оболонки.

Рядок

Що можна зробити, це або порівняння рядків, [ "$flag" == "true" ]або використання змінного вмісту в якійсь команді і перевірка його наслідків , як виконати true(оскільки є і виконуваний файл, який викликається, trueі один, який називається false) як команда, і перевірити, чи не є вихідний код цієї команди нульовим. (успішний).

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

Або коротше:

if "$flag"; then ... fi

Якщо вміст змінної використовується як команда, a !може бути використаний для заперечення стану виходу команди, якщо між обома ( ! cmd) існує пробіл , як у:

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

Сценарій слід змінити на:

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

Цілий

Використовуйте числові значення та арифметичні розширення .

У цьому випадку код виходу $((0))є 1і вихідний код $((1))є 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)розширеним глобальним оператором, ні zsh (за винятком емуляції sh), де він конфліктує glob(qualifiers)з bareglobqualі glob(group)без.


-1
[ ${FOO:-0} == 0 ] && FOO=1 || FOO=0

або навіть:

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

повинен виконати роботу

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.