Як я можу оголосити і використовувати булеві змінні в сценарії оболонки?


977

Я спробував оголосити булеву змінну в сценарії оболонки, використовуючи наступний синтаксис:

variable=$false

variable=$true

Це правильно? Крім того, якщо я хотів би оновити цю змінну, я використовував би той самий синтаксис? Нарешті, чи правильний наступний синтаксис використання булевих змінних як виразів?

if [ $variable ]

if [ !$variable ]

73
ПОДЕРЖАЙТЕ! trueі falseв контексті більшість фрагментів нижче тільки прості рядки, а НЕ то bash built-ins!!! Будь ласка, прочитайте відповідь Майка Хольта нижче. (Це один із прикладів, коли відповідь, що отримала високу оцінку і прийнята, - ІМХО заплутаний і затьмарює проникливий зміст у менших проголосованих відповідях)
mjv

7
@mjv Більшість плутанин щодо цього питання (і відповіді Міку) було пов’язано з тим, що Міку переглянув свою відповідь в якийсь момент після того, як було опубліковано декілька коментарів, в яких описувалося, як відповідь Міку викликав називання башти вбудованим true. Виявляється, оригінальна відповідь Міку справді зателефонувала до trueвбудованого, але переглянута відповідь не стала. Це спричинило те, що зазначені коментарі виявилися неправильними щодо того, як працював код Міку. Відповідь Міку відтоді редагується, щоб чітко відображати як оригінальний, так і переглянутий код. Сподіваємось, це ставить плутанину відпочити раз і назавжди
Майк Холт

2
[ true ] && echo yes, true is trueі (upppsss) [ false ] && echo yes, false is also true. / bin / true та / bin / false дає код повернення $? для функцій, не для порівняння.
fcm

Якщо встановлено var, variable=somethingніж це правда, якщо скинути, variable=що було б неправдиво [[ $variable ]] && echo true || echo falseта зворотно[[ ! $variable ]] && echo false || echo true
Іван

Відповіді:


1200

Переглянута відповідь (12 лютого 2014 р.)

the_world_is_flat=true
# ...do something interesting...
if [ "$the_world_is_flat" = true ] ; then
    echo 'Be careful not to fall off!'
fi

Оригінальний відповідь

Застереження: https://stackoverflow.com/a/21210966/89391

the_world_is_flat=true
# ...do something interesting...
if $the_world_is_flat ; then
    echo 'Be careful not to fall off!'
fi

Від: Використання булевих змінних у Bash

Причина, до якої включена оригінальна відповідь, полягає в тому, що коментарі до редакції 12 лютого 2014 року стосуються лише оригінальної відповіді, і багато коментарів помиляються, коли асоціюються з переглянутою відповіддю. Наприклад, коментар Денніса Вільямсона про баш, побудований true2 червня 2010 року, стосується лише оригінальної відповіді, а не переглянутої.


37
Щоб пояснити, що відбувається: ifоператор виконує вміст змінної, яка є вбудованою Bash true. Будь-яка команда може бути встановлена ​​як значення змінної, а її вихідне значення буде оцінено.
Призупинено до подальшого повідомлення.

7
@pms Оператори "-o" і "-a" призначені лише для команд "test" (він же "[]"). Натомість це "if + команда", без "тесту". (На кшталт "якщо файл grep foo; тоді ...".) Отже, використовуйте нормальні &&та ||оператори: # t1=true; t2=true; f1=false;# if $t1 || $f1; then echo is_true ; else echo is_false; fi; (повертає "true", оскільки t1 = true) # if $t1 && $f1 || $t2; then echo is_true ; else echo is_false; fi (повертає "true", оскільки t2 = true) . Знову ж таки, це ТОЛЬКО працює, тому що "true" / "false" - це баш-вбудовані (повернення true / false). Ви не можете використовувати "if $ var ...", якщо var не cmd (тобто істина чи false)
michael

14
-1, див. Мою відповідь для пояснення.
Денніс

3
Тут багато невірної інформації. / bin / true не використовується ефективно. Дивіться відповідь Денніса.
айк

1
Цей код не є однаковим і не працює так само, як пов'язана стаття. Зв'язаний код викликає програму за ім'ям, збереженим у змінній, але код у цій відповіді є лише порівнянням рядків.
питання Quolonel

794

TL; DR

bool=true

if [ "$bool" = true ]

Питання з відповіді Міку ( оригінальна )

Я не рекомендую приймати відповідь 1 . Його синтаксис досить, але він має деякі недоліки.

Скажімо, у нас є така умова.

if $var; then
  echo 'Muahahaha!'
fi

У наступних випадках 2 ця умова буде оцінена як справжня та виконана вкладена команда.

# Variable var not defined beforehand. Case 1
var=''  # Equivalent to var="".        Case 2
var=    #                              Case 3
unset var  #                           Case 4
var='<some valid command>'  #          Case 5

Зазвичай ви хочете, щоб ваша умова оцінювалась справжньою лише тоді, коли ваша "булева" змінна varв цьому прикладі явно встановлена ​​на істинне. Усі інші випадки небезпечно оманливі!

Останній випадок (№5) особливо неслухняний, оскільки він буде виконувати команду, що міститься в змінній (саме тому умова оцінюється як істинна для дійсних команд 3, 4 ).

Ось нешкідливий приклад:

var='echo this text will be displayed when the condition is evaluated'
if $var; then
  echo 'Muahahaha!'
fi

# Outputs:
# this text will be displayed when the condition is evaluated
# Muahahaha!

Цитування ваших змінних безпечніше, наприклад if "$var"; then. У вищезазначених випадках вам слід отримати попередження про те, що команда не знайдена. Але ми все ще можемо зробити краще (див. Мої рекомендації внизу).

Також див. Пояснення оригінальної відповіді Міку Майка Холта.

Питання з відповіддю Гбара

Цей підхід також має несподівану поведінку.

var=false
if [ $var ]; then
  echo "This won't print, var is false!"
fi

# Outputs:
# This won't print, var is false!

Ви б очікували, що вищевказана умова оцінюється як хибна, таким чином, ніколи не виконуючи вкладене твердження. Сюрприз!

Цитуючи значення ( "false"), цитуючи змінну ( "$var"), використовуючи testабо [[замість [, не змінювати.

Те , що я дійсно рекомендую:

Ось такі способи рекомендую вам перевірити свої "булеві". Вони працюють, як очікувалося.

bool=true

if [ "$bool" = true ]; then
if [ "$bool" = "true" ]; then

if [[ "$bool" = true ]]; then
if [[ "$bool" = "true" ]]; then
if [[ "$bool" == true ]]; then
if [[ "$bool" == "true" ]]; then

if test "$bool" = true; then
if test "$bool" = "true"; then

Всі вони майже рівнозначні. Вам доведеться набрати ще кілька натискань клавіш, ніж підходи в інших відповідях 5 , але ваш код буде більш захисним.


Виноски

  1. Відповідь Міку відтоді було відредаговано і більше не містить (відомих) вад.
  2. Не вичерпний список.
  3. Дійсна команда в цьому контексті означає команду, яка існує. Не має значення, правильно чи неправильно використовується команда. Напр., Як man womanі раніше, вважатимуться дійсною командою, навіть якщо такої сторінки не існує.
  4. Що стосується недійсних (неіснуючих) команд, Bash просто скаржиться, що команду не знайдено.
  5. Якщо ви дбаєте про довжину, перша рекомендація найкоротша.

8
Використання ==з портативом [або testйого не є. Вважаючи, що портативність є єдиною перевагою [/ testмає над [[, дотримуйтесь =.
чепнер

2
@Scott Я використовую рибу в якості моєї основної оболонки, яка має чітку мову сценаріїв порівняно з bash на мою думку.
Денніс

1
Так, я просто не зміг знайти в коментарях ніякої вдячності за цей прихований жарт, тому довелося вказати на це =)
Кранач

5
Для мене концептуально легше зрозуміти, якщо я використовую bool = "true". Тоді зрозуміло, що це просто рядок, а не якесь особливе значення чи вбудований.
wisbucky

1
@dolmen абсолютно, оцінка вводу не так ризикована, коли ви керуєте входом, але я все-таки вважаю це поганою практикою, якої слід уникати, якщо її легко уникнути. Хтось, хто коли-небудь бачив і використовував колишній стиль, може не знати про його вади, які можуть викликати несподівану поведінку.
Денніс

175

Тут, мабуть, є деякі непорозуміння щодо вбудованого Bash true, а точніше, щодо того, як Bash розширює та інтерпретує вирази всередині дужок.

Код у відповіді miku абсолютно не має нічого спільного з вбудованим Bash true, а /bin/trueтакож будь-яким іншим ароматом trueкоманди. У цьому випадку trueце не що інше, як звичайна символьна рядок, і жоден виклик trueкоманди / вбудованого не робиться ні за призначенням змінної, ні за допомогою оцінки умовного виразу.

Наступний код функціонально ідентичний коду у відповіді miku:

the_world_is_flat=yeah
if [ "$the_world_is_flat" = yeah ]; then
    echo 'Be careful not to fall off!'
fi

Тільки різниця полягає в тому , що чотири символи порівнюваних «у», «е», «а» і «ч» замість «т», «г», «і», і «е». Це воно. Немає спроб викликати команду чи вбудовану назву yeah, а також немає (на прикладі Міку) ніяких спеціальних операцій, коли Bash розбирає маркер true. Це просто рядок і зовсім довільний.

Оновлення (19.02.2014): Після переходу за посиланням у відповіді Міку, тепер я бачу, звідки йде певна плутанина. У відповіді Міку використовуються одинарні дужки, але фрагмент коду, на який він посилається, не використовує дужки. Це просто:

the_world_is_flat=true
if $the_world_is_flat; then
  echo 'Be careful not to fall off!'
fi

Обидва фрагменти коду будуть вести себе однаково, але дужки повністю змінюють те, що відбувається під капотом.

Ось що робить Bash у кожному випадку:

Немає дужок:

  1. Розгорніть змінну $the_world_is_flatдо рядка "true".
  2. Спроба проаналізувати рядок "true"як команду.
  3. Знайдіть і запустіть trueкоманду (вбудовану або /bin/trueзалежно від версії Bash).
  4. Порівняйте код виходу trueкоманди (який завжди дорівнює 0) з 0. Згадайте, що у більшості оболонок вихідний код 0 вказує на успіх, а все інше вказує на невдачу.
  5. Так як код виходу був 0 (успіх), виконати в ifзаяві , в thenположення

Дужки:

  1. Розгорніть змінну $the_world_is_flatдо рядка "true".
  2. Розбираємо тепер повністю розширений умовний вираз, який має форму string1 = string2. =Оператор в Bash порівняння рядків оператора. Тому...
  3. Зробіть порівняння рядків на "true"та "true".
  4. Так, дві струни були однаковими, тому значення умовного є істинним.
  5. Виконайте пункт ifзаяви then.

Код без дужок працює, тому що trueкоманда повертає вихідний код 0, що вказує на успіх. Скорочений код працює, тому що значення $the_world_is_flatідентичне буквеному trueрядку праворуч від =.

Для того, щоб повернути точку додому, розгляньте наступні два фрагменти коду:

Цей код (якщо він працює з root правами) перезавантажить ваш комп'ютер:

var=reboot
if $var; then
  echo 'Muahahaha! You are going down!'
fi

Цей код просто друкує "Приємна спроба". Команда перезавантаження не викликається.

var=reboot
if [ $var ]; then
  echo 'Nice try.'
fi

Оновлення (2014-04-14) Щоб відповісти на запитання у коментарях щодо різниці між =та ==: AFAIK, різниці немає. ==Оператор є Bash-специфічний синонім =, і, наскільки я бачив, вони працюють точно так же у всіх контекстах.

Однак зауважте, що я конкретно говорю про оператори порівняння =і ==рядків, що використовуються в [ ]або [[ ]]тестах. Я не стверджую , що =і ==є взаємозамінними всюди в Баш.

Наприклад, ви, очевидно, не можете виконати присвоєння змінної ==, наприклад var=="foo"((технічно це ви можете зробити, але значення varбуде "=foo", тому що Bash тут не бачить ==оператора, він бачить оператора =(призначення), а потім буквальне значення ="foo", яке щойно стає "=foo").

Крім того, хоча вони =і ==взаємозамінні, ви повинні мати на увазі, що те, як працюють ці тести , залежить від того, використовуєте ви його всередині, [ ]або [[ ]], а також від того, цитуються операнди чи ні. Докладніше про це можна прочитати в Посібнику з розширеного сценарію Bash: 7.3 Інші оператори порівняння (прокрутіть до обговорення =та ==).


Перевага без дужок також має перевагу, щоб ви могли писати чисті, чіткі (imo) однолінійки на зразок$the_world_is_flat && echo "you are in flatland!"
ajk

9
Правда. Хоча я не виступаю за (або проти) жодного підходу. Я просто хотів прояснити деякі дезінформації, за які тут проголосують, щоб люди, які пізніше натрапляють на цю тему, не пішли з купою помилок про те, як це все працює.
Майк Холт

1
Причиною плутанини є те, що оригінальна відповідь міку стояла 4 роки. Всі посилання на вбудовані trueбули зроблені щодо оригінальної відповіді. (Переглянута відповідь 12 лютого 2014 року не була подана miku.) Я змінив відповідь, щоб включити як оригінальну, так і перероблену. Тоді коментарі людей мають сенс.
вісбукі

1
Прочитавши запропоновані тут відповіді, у мене складається враження, що немає такого, як насправді використовувати реальне true. Чи є спосіб? Я підозрюю, що багато програмістів, які використовують жорсткіші мови, переглядають цю відповідь, щоб допомогти їм змішати bashклей, щоб полегшити їх життя, хотів би ===оператор, так що струни та "булеві" насправді не є взаємозамінними. Якщо вони просто дотримуватися 0 і 1 і використання , (( $maybeIAmTrue ))як запропоновано в Quolonel Питання «s відповідь ?
SeldomNeedy

2
Щоб звернутися до коментаря SeldomNeedy, так, ви можете використовувати реальне true, але взагалі не як щось для порівняння змінної з, оскільки реальність trueсама по собі не має значення. Все, що він робить, - це встановити статус виходу на 0, що свідчить про успіх. Варто відзначити, що він по суті еквівалент так званій "нульовій команді", або :. Що стосується використання 0та 1, саме це я роблю у всіх своїх сценаріях сьогодні, де мені потрібні булеви. І я використовую (( ))оператор замість того, [[ ]]щоб оцінювати. Так, наприклад, якщо у мене є flag=0, то я можу це зробитиif (( flag )); then ...
Майк Холт

57

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

#!/bin/bash

false=0
true=1

((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true

Вихід:

справжній
не хибний


3
плюси: (1.) поведінка схожа зі способом обробки булів, (2.) синтаксис дуже стислий / мінімальний (не вимагає правої змінної та операторів типу '=' або '=='), (3 .) <суб'єктивний> для мене я розумію, що відбувається без тривалого пояснення ... на відміну від відповідей Міку та Денніса, які, як видається, вимагають довго закручених пояснень </subjective>
Тревор Бойд Сміт

3
@TrevorBoydSmith Чому ти просто не сказав: "плюси: все, мінуси: нічого". Заощаджує амортизаційні витрати на клавіатурі та моніторі в довгостроковій перспективі.
питання Quolonel

4
Для інтерактивного використання, як однолінійки, обов’язково залиште пробіл після !цього, інакше це зробить розширення історії. ((! foo))працює, так і робить ! ((foo)). Я люблю це рішення, BTW. Нарешті короткий спосіб зробити булеві змінні. ((foo || bar))працює як очікувалося.
Пітер Кордес

5
(())розширює змінні рекурсивно, чого я не очікував. foo=bar; bar=baz; ((foo)) && echo echoнічого не друкує, але це правда з baz=1. Таким чином , ви можете підтримувати foo=trueі foo=falseяк 0 або 1, роблячи true=1.
Пітер Кордес

2
@quolonel дякую за дуже корисний ресурс. Звичайно, моє розуміння обмежене - це людська природа бути обмеженою в усіх розуміннях незалежно від сфери. Однак ви не хотіли б сказати мені, яке з моїх тверджень призводить вас до того, що моє розуміння цієї конкретної справи є неповним?
Hubert Grzeskowiak

34

Довга історія:

Булів немає в Баші

Bash має булеві вирази щодо порівняння та умов. Це означає, що ви можете оголосити та порівняти в Bash - це рядки та цифри. Це воно.

Де б ви не бачили trueабо falseв Bash, це або рядок, або команда / вбудований, який використовується лише для його вихідного коду.

Цей синтаксис ...

if true; then ...

по суті ...

if COMMAND; then ...

Ця умова справедлива щоразу, коли команда повертає код виходу 0. trueІ falseє вбудованими Bash, а іноді й окремими програмами, які не роблять нічого, крім повернення відповідного коду виходу.

Умовне вище є еквівалентним:

COMMAND && ...

Використовуючи квадратні дужки або testкоманду, ви покладаєтесь на код виходу цієї конструкції. Майте на увазі , що [ ]і [[ ]]також в декількох команд / вбудовані функції, як і будь-який інший. Тому ...

if [[ 1 == 1 ]]; then echo yes; fi

відповідає

if COMMAND; then echo yes; fi

і COMMANDось це[[ 1 == 1 ]]

if..then..fiКонструкція просто синтаксичний цукор. Ви завжди можете просто запустити команди, розділені подвійною амперсом та для того ж ефекту:

[[ 1 == 1 ]] && echo yes

Під час використання trueта falseв цих тестових конструкціях ви фактично передаєте лише рядок "true"або "false"команду тестування. Ось приклад:

Вірите чи ні, але всі ці умови дають однаковий результат :

if [[ false ]]; then ...
if [[ "false" ]]; then ...
if [[ true ]]; then ...
if [[ "true" ]]; then ...

TL; DR; завжди порівнюйте з рядками або числами

Щоб зробити це зрозумілим майбутнім читачам, я б рекомендував завжди використовувати цитати навколо trueта false:

ДО

if [[ "${var}" == "true" ]]; then ...
if [[ "${var}" == "false" ]]; then ...
if [[ -n "${var:-}" ]]; then echo "var is not empty" ...

НЕ ДАЙТЕ

if [ ... ]; then ...  # Always use double square brackets in bash!
if [[ "${var}" ]]; then ...  # This is not as clear or searchable as -n
if [[ "${var}" != true ]]; then ...  # Creates impression of Booleans
if [[ "${var}" -eq "true" ]]; then ...  # `-eq` is for numbers and doesn't read as easy as `==`

Можливо

if [[ "${var}" != "true" ]]; then ...  # Creates impression of Booleans. It can be used for strict checking of dangerous operations. This condition is false for anything but the literal string "true".

Я вважаю за краще використовувати Tта Fуточнити, що це не справжні булеві значення.
phk

1
Я не можу погодитися з тим, що "завжди використовуйте подвійні дужки в басі". Насправді майже у всіх написаних сценаріях я використовую окремі дужки, за винятком випадків, коли мені потрібно виконати відповідність шаблонів. Я думаю, що слід розуміти різницю між [(тобто test) [[і використовувати той, який підходить для його потреби.
Вейджун Чжоу

@WeijunZhou розуміємо, в яких випадках кращі дужки краще?
Хуберт Ґжесков'як

Це більше особистого смаку, я просто вважаю, що занадто сміливо говорити "Завжди використовуйте подвійні квадратні дужки в баш". Але є деякі крайні випадки, якими я користувався. Одиничні дужки дозволяють вказати сам тест у варі. В якості спрощеного прикладу розглянемоif ....; then mytest='-gt'; else mytest='-eq'; fi; #several lines of code; if [ "$var1" "$mytest" "$var2" ]; then ...; fi
Вейджун Чжоу

@WeijunZhou Ваш приклад є сильним аргументом проти одиничних квадратних дужок. Це робить код набагато важчим для розуміння та відкриває вікно, широко відкрите для помилок. Подвійні дужки є більш суворими і заохочують більш чистий код.
Губерт Ґжесков'як

18

Давним-давно, коли все, що у нас було sh, булеви, де оброблялися, покладаючись на конвенцію testпрограми, де testповертає помилковий статус виходу, якщо працювати без будь-яких аргументів.

Це дозволяє думати про змінну, яка не встановлена ​​як помилкова та змінна, встановлена ​​на будь-яке значення як істинне. Сьогодні testце вбудований в Bash і широко відомий своїм односимвольним псевдонімом [(або виконуваним файлом для використання в оболонках, яких не вистачає, як зазначає dolmen):

FLAG="up or <set>"

if [ "$FLAG" ] ; then
    echo 'Is true'
else
    echo 'Is false'
fi

# Unset FLAG
#    also works
FLAG=

if [ "$FLAG" ] ; then
    echo 'Continues true'
else
    echo 'Turned false'
fi

Через цитування умов, сценаристи вважають за краще використовувати складну команду, [[яка імітує test, але має приємніший синтаксис: змінні з пробілами не потрібно цитувати; можна використовувати &&і ||як логічні оператори із дивним пріоритетом, і в обмеженнях кількості термінів немає обмежень POSIX.

Наприклад, щоб визначити, чи встановлено FLAG, а COUNT - число більше 1:

FLAG="u p"
COUNT=3

if [[ $FLAG  && $COUNT -gt '1' ]] ; then
    echo 'Flag up, count bigger than 1'
else
    echo 'Nope'
fi

Цей матеріал може заплутатися, коли потрібні пробіли, рядки нульової довжини та нульові змінні, а також коли ваш сценарій повинен працювати з декількома оболонками.


3
[це не просто псевдонім всередині bash. Цей псевдонім також існує як двійковий файл (або як посилання, що вказує на) і може використовуватися з голою sh. Перевірити ls -l /usr/bin/\[. З bash/ zshви повинні використовувати [[це справжній чистий внутрішній і є набагато більш потужним.
долмен

1
@dolmen [і testтакож Bash SHELL BUILTIN COMMAND згідно сторінці Баш вручну, так що не повинно бути проблемою в роботі. Те саме з напр. Тире. (/ bin / sh може бути просто символьним посиланням на / bin / dash). Для використання виконуваного файлу ви повинні використовувати повний шлях, тобто /usr/bin/\[.
jarno

12

Як я можу оголосити і використовувати булеві змінні в сценарії оболонки?

На відміну від багатьох інших мов програмування, Bash не відокремлює свої змінні за "типом". [1]

Тож відповідь досить зрозуміла. У Bash немає жодної булевої змінної .

Однак:

За допомогою заяви заяви ми можемо обмежити присвоєння значення змінним. [2]

#!/bin/bash
declare -ir BOOL=(0 1) # Remember BOOL can't be unset till this shell terminates
readonly false=${BOOL[0]}
readonly true=${BOOL[1]}

# Same as declare -ir false=0 true=1
((true)) && echo "True"
((false)) && echo "False"
((!true)) && echo "Not True"
((!false)) && echo "Not false"

rВаріант в declareі readonlyвикористовується для стану явно , що змінні тільки для читання . Сподіваюся, мета зрозуміла.


1
Чому ти просто не робиш declare -ir false=0 true=1? Яка перевага використання масиву?
Бенджамін В.

@BenjaminW. Я просто хотів згадати про rпараметр і readonlyкоманду. Я зробив би це так, як ви запропонували в моїх сценаріях
sjsam

можливо, я щось пропустив, але чому неправдиві та неправдиві заявлені таким чином знаки долара використовують? $ true $ false
qodeninja

Буквально просто скопіювавши свою відповідь і зробивши її гіршою.
питання

@QuolonelQuestions Змінні Bash не набираються , отже, немає сенсу говорити. declare and use boolean variablesМи могли б просто, більш ніж одним способом, імітувати / припускати, що змінна має тип . Я ніде не бачив того, що згадувалося у вашій відповіді.
sjsam

10

Замість того, щоб підробляти булеві та залишати пастку для майбутніх читачів, чому б просто не використати кращу цінність, ніж істинна та хибна?

Наприклад:

build_state=success
if something-horrible; then
  build_state=failed
fi

if [[ "$build_state" == success ]]; then
  echo go home; you are done
else
  echo your head is on fire; run around in circles
fi

чому б не цілі числа?
phil294

3
@Blauhirn, оскільки цілі числа використовуються по-різному в залежності від мов. У деяких мовах 0примушують до falseі 1до true. Що стосується кодів виходу з програми (який історично використовує bash), це 0позитивний результат, або trueвсе інше - негативний / помилка або false.
Hubert Grzeskowiak

7

POSIX (інтерфейс портативної операційної системи)

Я пропускаю тут ключовий момент, який полягає в мобільності. Тому мій заголовок має POSIX у собі.

За суті, все голосували відповіді правильні, за винятком вони Bash -специфічні занадто багато.

В основному я хочу лише додати більше інформації про портативність.


  1. [ і ] дужки типу in [ "$var" = true ]не потрібні, і ви можете їх опустити і використовувати testкоманду безпосередньо:

    test "$var" = true && yourCodeIfTrue || yourCodeIfFalse

    Важлива примітка: я більше не рекомендую це робити, оскільки це повільно застаріло і складніше поєднувати кілька заяв.

  2. Уявіть собі , що ці слова trueі falseозначає оболонки, тест самі:

    echo $(( true ))
    0
    echo $(( false ))
    1

    Але використовуючи лапки:

    echo $(( "true" ))
    bash: "true": syntax error: operand expected (error token is ""true"")
    sh (dash): sh: 1: arithmetic expression: expecting primary: ""true""

    Те саме стосується:

    echo $(( "false" ))

    Оболонка не може інтерпретувати її, крім рядка. Я сподіваюся, що ви отримуєте уявлення про те, наскільки добре це використання належного ключового слова без лапок .

    Але ніхто не говорив про це у попередніх відповідях.

  3. Що це означає? Ну, кілька речей.

    • Ви повинні звикнути до булевих ключових слів, насправді трактуються як числа, тобто true= 0і false= 1, пам’ятайте, що всі ненульові значення трактуються якfalse .

    • Оскільки вони розглядаються як числа, ви також повинні ставитися до них так, тобто якщо ви визначаєте змінну, скажімо:

      var_bool=true
      echo "$var_bool"
       true

      ви можете створити протилежне його значення за допомогою:

      var_bool=$(( 1 - $var_bool ))  # same as $(( ! $var_bool ))
      echo "$var_bool"
      1

    Як ви самі бачите, оболонка робить друкований trueрядок вперше, коли ви його використовуєте, але з тих пір це все працює через число, що 0представляє trueабо 1представляє falseвідповідно.


Нарешті, що слід робити з усією цією інформацією

  • По-перше, одна добра звичка буде призначати 0замість true; 1замістьfalse .

  • Другою доброю звичкою було б перевірити, чи змінна є / не дорівнює нулю:

    if [ "$var_bool" -eq 0 ]; then
         yourCodeIfTrue
    else
         yourCodeIfFalse
    fi

6

Щодо синтаксису, то це проста методика, яку я використовую (на прикладі) для послідовного та добросовісного управління булевою логікою:

# Tests
var=
var=''
var=""
var=0
var=1
var="abc"
var=abc

if [[ -n "${var}" ]] ; then
    echo 'true'
fi
if [[ -z "${var}" ]] ; then
    echo 'false'
fi

# Results
# var=        # false
# var=''      # false
# var=""      # false
# var=0       # true
# var=1       # true
# var="abc"   # true
# var=abc     # true

Якщо змінна ніколи не оголошується, відповідь така: # false

Отже, простим способом встановити змінну в true (використовуючи цю методологію синтаксису) було б var=1; навпаки,var='' .

Довідка:

-n = Вірно, якщо довжина рядка var не дорівнює нулю.

-z = Вірно, якщо довжина рядка var дорівнює нулю.


5

У багатьох мовах програмування булівський тип є або реалізується як підтип цілого числа, де він trueповодиться як 1і falseповодиться як 0:

Математично булева алгебра нагадує цілочисельну арифметичну модуль 2. Тому, якщо мова не забезпечує рідний булевий тип, найбільш природним та ефективним рішенням є використання цілих чисел. Це працює майже з будь-якою мовою. Наприклад, у Bash ви можете:

# val=1; ((val)) && echo "true" || echo "false"
true
# val=0; ((val)) && echo "true" || echo "false"
false

man bash :

((вираз))

Вираз оцінюється за правилами, описаними нижче в АРИТМЕТИЧНІЙ ОЦІНКІ. Якщо значення виразу не дорівнює нулю, стан повернення дорівнює 0; інакше статус повернення дорівнює 1. Це абсолютно рівнозначно вираженню "вираз".


5

Білл Паркер отримує голосування , оскільки його визначення відмінені від звичайної конвенції коду. Як правило, true визначається як 0, а false - ненульовий. 1 буде працювати для помилкових, як 9999 та -1. Те саме з функціями повернення значень - 0 - це успіх, і все, що не має нуля, - це збій. Вибачте, я ще не маю довіри на вулиці, щоб проголосувати або відповісти на нього безпосередньо.

Bash рекомендує використовувати подвійні дужки як звичку замість одинарних дужок, а посилання, яке надав Майк Холт, пояснює відмінності в тому, як вони працюють. 7.3. Інші оператори порівняння

З одного боку, -eqце чисельний оператор, тому маючи код

#**** NOTE *** This gives error message *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then

видасть заяву про помилку, очікуючи цілого виразу. Це стосується будь-якого параметра, оскільки жодне не є цілим значенням. Однак якщо ми поставимо навколо нього подвійні дужки, він не видасть заяву про помилку, але дасть неправильне значення (ну, у 50% можливих перестановок). Він оцінюватиметься як [[0 -eq true]] = успіх, але також і [[0 -eq false]] = успіх, що невірно (хммм .... як щодо цього вбудованого чисельного значення?).

#**** NOTE *** This gives wrong output *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then

Є й інші умовні перестановки, які також дадуть неправильний вихід. В основному, що завгодно (крім переліченої вище умови помилки), яка встановлює змінну чисельному значенню та порівнює її з істинним / хибним вбудованим, або встановлює змінну істинним / хибним вбудованим і порівнює її з числовим значенням. Крім того, все, що встановлює змінну на справжній / хибний вбудований і робить порівняння, використовуючи -eq. Тому уникайте -eqбулевих порівнянь та уникайте використання числових значень для булевих порівнянь. Ось підсумок перестановок, які дадуть недійсні результати:

# With variable set as an integer and evaluating to true/false
# *** This will issue error warning and not run: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then

# With variable set as an integer and evaluating to true/false
# *** These statements will not evaluate properly: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
#
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" == true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then


# With variable set as an true/false builtin and evaluating to true/false
# *** These statements will not evaluate properly: *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = 0 ]; then
#
if [[ "${The_world_is_flat}" = 0 ]]; then
#
if [ "${The_world_is_flat}" == 0 ]; then
#
if [[ "${The_world_is_flat}" == 0 ]]; then

Отже, тепер до того, що працює. Використовуйте справжні / хибні вбудовані для порівняння та оцінок (як зазначив Майк Хант, не вкладайте їх у лапки). Потім використовуйте або один, або подвійний знак рівності (= або ==) і одно- або подвійні дужки ([] або [[]]). Особисто мені подобається знак подвійних рівностей, тому що він нагадує мені логічні порівняння в інших мовах програмування, і подвійні лапки лише тому, що мені подобається вводити текст. Отже, ці роботи:

# With variable set as an integer and evaluating to true/false
# *** These statements will work properly: *****
#
The_world_is_flat=true/false;
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then

Там у вас є.


2
В true/ falseвбудовані модулі не використовуються тут (ігнорувати те , що підсвічування синтаксису деяких редакторів може означати), особливо в тих […]випадках , ви можете думати про це як проста рядок тут (той , який дається в якості параметра в [команді).
phk

У вас це є зараз.
Пітер Мортенсен

4

Мої висновки та пропозиції дещо відрізняються від інших публікацій. Я виявив, що я можу в основному використовувати "булі", як в будь-якій "звичайній" мові, без запропонованих "стрибків з обручем" ...

Немає потреби в []явних порівняннях рядків ... Я спробував декілька дистрибутивів Linux. Я протестував Bash, Dash і BusyBox . Результати завжди були однаковими. Я не впевнений, про що говорять у первинних голосуючих публікаціях. Можливо, часи змінилися, і це все, що там є?

Якщо встановити змінну true, вона згодом оцінюється як "стверджувальна" в умовному режимі. Встановіть його false, і він оцінить як "негатив". Дуже прямо! Єдине застереження - це те, що невизначена змінна також оцінює як істинну ! Було б добре, якби це зробило навпаки (як це було б у більшості мов), але це трюк - вам просто потрібно явно ініціалізувати свої булеві на істинне чи хибне .

Чому це працює саме так? Ця відповідь у два рази. A) true / false в оболонці справді означає "без помилки" проти "помилки" (тобто 0 проти будь-чого іншого). B) true / false не є значеннями, а швидше твердженнями в сценарії оболонок! Щодо другої точки, виконання trueабо falseна рядку самостійно встановлює значення повернення для блоку, в якому ви знаходитесь, до цього значення, тобто falseє оголошенням "помилки, що виникає", де справжня "очищує" це. Використовуючи його з присвоєнням змінній "повертає" цю змінну. An невизначені змінні оцінює , як trueв умовному тому , що в рівній мірі має значення 0 або «ні» сталася помилка.

Дивіться приклад рядків та результати Bash нижче. Перевірте це самостійно, якщо хочете підтвердити ...

#!/bin/sh

# Not yet defined...
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;

myBool=true
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;

myBool=false
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;

Врожайність

when set to
it evaluates to true
when set to true
it evaluates to true
when set to false
it evaluates to false

1

Ось простий приклад, який працює для мене:

temp1=true
temp2=false

if [ "$temp1" = true ] || [ "$temp2" = true ]
then
    echo "Do something." 
else
    echo "Do something else."
fi

1

Ось реалізація короткої руки if true.

# Function to test if a variable is set to "true"
_if () {
    [ "${1}" == "true" ] && return 0
    [ "${1}" == "True" ] && return 0
    [ "${1}" == "Yes" ] && return 0
    return 1
}

Приклад 1

my_boolean=true

_if ${my_boolean} && {
    echo "True Is True"
} || {
    echo "False Is False"
}

Приклад 2

my_boolean=false
! _if ${my_boolean} && echo "Not True is True"

Так, функціональне розкладання недооцінене.
Пітер Мортенсен

1

Я вважав наявні відповіді заплутаними.

Особисто мені просто хочеться мати щось подібне до C.

Цей фрагмент працює багато разів на день у виробництві:

snapshotEvents=true

if ($snapshotEvents)
then
    # Do stuff if true
fi

і щоб все було щасливим, я перевірив:

snapshotEvents=false

if !($snapshotEvents)
then
    # Do stuff if false
fi

Що також добре працювало.

$snapshotEventsОцінює вміст значення змінної. Тож вам потрібна $.

Дуже не потрібні дужки, я просто вважаю їх корисними.


2
Де ви вилучите дужки, це оригінальна відповідь @ miku вгорі.
долмен

1
Без дужок вираз не оцінюється.
буде

@ буде так, це так. Вам не потрібні () s.
phil294

1
@Blauhirn ... Привіт, я базував свої коментарі на експериментах з GNU Bash на комп'ютері Linux Mint / Ubuntu. Ви, напевно, маєте рацію в теорії - () вони не потрібні. Моя єдина відповідь - це спробувати її, схоже, це залежить від версії Bash, власне вираження чи контексту тощо.
буде

1

Ось покращення щодо оригінальної відповіді Міку, яка стосується занепокоєння Денніса Вільямсона щодо випадку, коли змінна не встановлена:

the_world_is_flat=true

if ${the_world_is_flat:-false} ; then
    echo "Be careful not to fall off!"
fi

І перевірити, чи є змінна false:

if ! ${the_world_is_flat:-false} ; then
    echo "Be careful not to fall off!"
fi

Щодо інших випадків із неприємним вмістом у змінній, це проблема з будь-яким зовнішнім входом, який подається програмі.

Будь-який зовнішній вхід повинен бути підтверджений, перш ніж довіряти йому. Але цю перевірку потрібно зробити лише один раз, коли цей вхід буде отримано.

Це не повинно впливати на ефективність програми, роблячи це при кожному використанні змінної, як пропонує Денніс Вільямсон .


1

Ви можете використовувати shFlags .

Це дає можливість визначити: DEFINE_bool

Приклад:

DEFINE_bool(big_menu, true, "Include 'advanced' options in the menu listing");

З командного рядка можна визначити:

sh script.sh --bigmenu
sh script.sh --nobigmenu # False

GFlags не має сенсу в цій відповіді - це бібліотека C ++. Його не можна безпосередньо використовувати в скриптах оболонки.
Джонатан Хрест

Оновлена ​​відповідь на shFlags, який є портом GFlags до оболонки.
gogasca

0

Це тест на швидкість щодо різних способів тестування "булевих" значень у Bash:

#!/bin/bash
rounds=100000

b=true # For true; b=false for false
type -a true
time for i in $(seq $rounds); do command $b; done
time for i in $(seq $rounds); do $b; done
time for i in $(seq $rounds); do [ "$b" == true ]; done
time for i in $(seq $rounds); do test "$b" == true; done
time for i in $(seq $rounds); do [[ $b == true ]]; done

b=x; # Or any non-null string for true; b='' for false
time for i in $(seq $rounds); do [ "$b" ]; done
time for i in $(seq $rounds); do [[ $b ]]; done

b=1 # Or any non-zero integer for true; b=0 for false
time for i in $(seq $rounds); do ((b)); done

Це надрукувало б щось подібне

true is a shell builtin
true is /bin/true

real    0m0,815s
user    0m0,767s
sys     0m0,029s

real    0m0,562s
user    0m0,509s
sys     0m0,022s

real    0m0,829s
user    0m0,782s
sys     0m0,008s

real    0m0,782s
user    0m0,730s
sys     0m0,015s

real    0m0,402s
user    0m0,391s
sys     0m0,006s

real    0m0,668s
user    0m0,633s
sys     0m0,008s

real    0m0,344s
user    0m0,311s
sys     0m0,016s

real    0m0,367s
user    0m0,347s
sys     0m0,017s

-2

Альтернативно - використовувати функцію

is_ok(){ :;}
is_ok(){ return 1;}
is_ok && echo "It's OK" || echo "Something's wrong"

Визначити функцію менш інтуїтивно, але перевірити її повернене значення дуже просто.


1
Це не змінна, яку ви могли б перевірити, а постійна функція
jarno

@jarno Чи тестування поверненого значення функції відрізняється від тестування змінної для цілей сценарію?
johnraff

Ну, питання стосується змінних.
jarno

Правда, хоча використання в сценарії оболонки було б однаковим.
johnraff

-2

Bash дійсно заплутує питання з подібними [, [[, ((, $((і т.д.

Усі крокують на кодових просторах один одного. Я здогадуюсь це здебільшого історичне, де Баш мусив робити вигляд, що буває shчас від часу.

Більшу частину часу я можу просто вибрати метод і дотримуватися його. У цьому випадку я схильний декларувати (бажано, у загальному бібліотечному файлі, який я можу включити .у свій фактичний сценарій (и)).

TRUE=1; FALSE=0

Тоді я можу використовувати ((... ))арифметичний оператор для перевірки таким чином.

testvar=$FALSE

if [[ -d ${does_directory_exist} ]]
then
    testvar=$TRUE;
fi

if (( testvar == TRUE )); then
    # Do stuff because the directory does exist
fi
  1. Ви повинні бути дисциплінованими. Ви testvarповинні бути налаштовані на $TRUEабо $FALSEпостійно.

  2. У ((... ))компараторах вам не потрібні попередні $, що робить його більш читабельним.

  3. Я можу використовувати ((... ))бо $TRUE=1і $FALSE=0, наприклад , числові значення.

  4. Мінусом доводиться $епізодично використовувати :

    testvar=$TRUE

    що не так симпатично.

Це не ідеальне рішення, але воно охоплює кожен випадок, коли мені потрібен такий тест.


2
Ви повинні оголосити свої константи повністю. Також будь-коли завжди використовуйте фігурні дужки при використанні змінних. Це конвенція, яку кожен повинен дотримуватися IMHO. Великий недолік цього рішення полягає в тому, що ви не можете змішувати алгебраїчний вираз з тестовими прапорами або зіставленнями рядків.
Hubert Grzeskowiak
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.