Как я могу объявить и использовать логические переменные в сценарии оболочки?


981

Я попытался объявить логическую переменную в сценарии оболочки, используя следующий синтаксис:

variable=$false

variable=$true

Это правильно? Кроме того, если бы я хотел обновить эту переменную, я бы использовал тот же синтаксис? Наконец, правильный ли следующий синтаксис для использования булевых переменных в качестве выражений?

if [ $variable ]

if [ !$variable ]

73
BEWARE! trueи falseв контексте большинство фрагментов ниже только простые строки, а не то bash built-ins!!! Пожалуйста, прочитайте ответ Майка Холта ниже. (Это один из примеров, когда высоко оцененный и принятый ответ ИМХО сбивает с толку и затеняет проницательный контент в менее проголосовавших ответах)
mjv

7
@mjv Большая часть путаницы по этому вопросу (и ответу Мику) была вызвана тем, что Мику пересмотрел свой ответ в какой-то момент после того, как было опубликовано несколько комментариев, описывающих, как ответ Мику включал вызов встроенного bash true. Оказывается, первоначальный ответ Мику действительно называл trueвстроенным, но пересмотренный ответ - нет. Это привело к тому, что указанные комментарии оказались неверными в отношении того, как работает код Мику. С тех пор ответ Мику был отредактирован для явного отображения как исходного, так и пересмотренного кода. Надеемся, что это ставит в тупик путаницу раз и навсегда.
Майк Холт

2
[ true ] && echo yes, true is trueи (Upppsss) [ false ] && echo yes, false is also true. / bin / true и / bin / false дают код возврата $? для функций не для сравнения.
фм

Если переменная установлена, тогда variable=somethingона истинна, если не установлена variable=, будет ложно [[ $variable ]] && echo true || echo falseи наоборот[[ ! $variable ]] && echo false || echo true
Иван

Ответы:


1203

Пересмотренный ответ (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 года относятся только к первоначальному ответу, и многие комментарии неверны, когда связаны с пересмотренным ответом. Например, комментарий Денниса Уильямсона о bash, встроенном true2 июня 2010 года, относится только к первоначальному ответу, а не к пересмотренному.


37
Чтобы объяснить, что происходит: ifоператор выполняет содержимое переменной, которая является встроенной в Bash true. Любая команда может быть установлена ​​как значение переменной, и ее выходное значение будет оценено.
Приостановлено до дальнейшего уведомления.

7
@pms Операторы "-o" и "-a" предназначены только для команды "test" (иначе "[]"). Вместо этого это «если + команда», без «теста». (Например, «if grep foo file; then ...».) Итак, используйте операторы normal &&и ||: # 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» являются встроенными в bash (возвращая true / false). Вы не можете использовать «if $ var ...», если var не является cmd (то есть true или false)
Майкл

14
-1, см. Мой ответ для объяснения.
Деннис

3
Здесь много неверной информации. / bin / true не используется эффективно. Смотрите ответ Денниса.
ajk

1
Этот код не совпадает и не работает так же, как ссылка на статью. Связанный код вызывает программу по имени, хранящемуся в переменной, но код в этом ответе - просто сравнение строк.
Quolonel Вопросы

795

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

Как правило, вы хотите, чтобы ваше условие оценивалось как true, когда ваша «логическая» переменная varв этом примере явно установлена ​​в true. Все остальные случаи опасно вводят в заблуждение!

Последний случай (# 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. В вышеуказанных случаях вы должны получить предупреждение о том, что команда не найдена. Но мы все еще можем сделать лучше (см. Мои рекомендации внизу).

Также см. Объяснение Майка Холта о первоначальном ответе Мику.

Проблемы с ответом Hbar

Этот подход также имеет неожиданное поведение.

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

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

Можно ожидать, что вышеприведенное условие оценивается как ложное, поэтому никогда не выполняете вложенный оператор. Сюрприз!

Кавычка value ( "false"), кавычка переменной ( "$var"), или использование testили [[вместо [, не имеют значения.

То , что я действительно рекомендую:

Вот способы, которые я рекомендую вам проверить ваши "Booleans". Они работают как положено.

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имеет более [[, придерживайтесь =.
Chepner

2
@ Scott Я использую fish в качестве основной оболочки, в которой, на мой взгляд, есть здравый язык сценариев по сравнению с bash.
Деннис

1
Да, я просто не смог найти в комментариях никакой оценки за эту скрытую шутку, поэтому должен был указать на это =)
Kranach

5
Для меня концептуально легче понять, если я использую bool = "true". Тогда ясно, что это просто строка, а не какое-то специальное значение или встроенная функция.
Висбуки

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

176

Кажется, здесь есть некоторое недопонимание относительно встроенной функции Bash true, а более конкретно, о том, как Bash расширяет и интерпретирует выражения в квадратных скобках.

Код в ответе miku не имеет ничего общего ни со встроенной системой Bash true, /bin/trueни с каким-либо другим видом trueкоманды. В этом случае trueэто не что иное, как простая символьная строка, и никакой вызов trueкоманды / встроенной функции никогда не выполняется ни с помощью присваивания переменной, ни с помощью вычисления условного выражения.

Следующий код функционально идентичен коду в ответе мику:

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

Только разница состоит в том , что четыре символа сравниваемых «у», «е», «а» и «ч» вместо «т», «г», «и», и «е». Вот и все. Не было предпринято никаких попыток вызвать команду или встроенный объект named yeah, а также (в примере miku) никакой особой обработки не происходит, когда Bash анализирует токен true. Это просто строка, причем совершенно произвольная.

Обновление (2014-02-19): после перехода по ссылке в ответе мику, теперь я вижу, откуда происходит некоторая путаница. В ответе Мику используются одиночные скобки, но фрагмент кода, на который он ссылается, не использует скобки. Это просто:

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").

Кроме того, хотя =и ==являются взаимозаменяемыми, вы должны иметь в виду, что как эти тесты работают , зависит от того, используете ли вы его внутри [ ]или [[ ]], а также от того, заключены ли в кавычки операнды. Вы можете прочитать больше об этом в Advanced Bash Scripting Guide: 7.3 Другие операторы сравнения (прокрутите вниз до обсуждения =и ==).


Подход без скобок также имеет преимущество, заключающееся в том, что вы можете писать чистые, ясные (imo) однострочные $the_world_is_flat && echo "you are in flatland!"
строки,

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

1
Причина путаницы в том, что первоначальный ответ Мику стоял 4 года. Все ссылки на встроенные trueбыли сделаны относительно оригинального ответа. (Пересмотренный ответ 12 февраля 2014 г. не был представлен miku.) Я отредактировал ответ, включив в него как оригинал, так и пересмотренный. Тогда комментарии людей имеют смысл.
wisbucky

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.) поведение схоже с тем, как C обрабатывает bools, (2.) синтаксис очень лаконичен / минимален (не требует правой переменной и таких операторов, как '=' или '=='), (3 .) <субъективно> для меня я понимаю, что происходит без длинных многословных объяснений ... в отличие от ответов Мику и Денниса, которые, похоже, требуют длинных многословных объяснений </ subjective>
Тревор Бойд Смит

3
@TrevorBoydSmith Почему вы просто не сказали «плюсы: все, минусы: ничего». Сэкономит амортизационные отчисления на клавиатуре и мониторе в долгосрочной перспективе.
Вопросы Quolonel

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

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

2
@quolonel спасибо за очень полезный ресурс. Конечно, мое понимание ограничено - человеческая природа ограничена во всем понимании независимо от предметной области. Тем не менее, не могли бы вы сказать мне, какие из моих утверждений приводят вас к предположению, что мое понимание этого конкретного вопроса является неполным?
Хуберт Гжесковяк

35

Короче говоря:

В Bash нет логических значений

У Bash есть логические выражения с точки зрения сравнения и условий. Тем не менее, то, что вы можете объявить и сравнить в Bash, это строки и числа. Вот и все.

Везде, где вы видите trueилиfalse в Bash, это либо строка, либо команда / встроенная команда, которая используется только для кода выхода.

Этот синтаксис ...

if true; then ...

по сути ...

if COMMAND; then ...

Условие истинно, когда команда возвращает код выхода 0. trueиfalse являются Баш встроенных команды , а иногда также отдельными программы , которые не делают ничего , кроме возвращения соответствующего кода выхода.

Условие выше эквивалентно:

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
Я не могу согласиться с "всегда используйте двойные скобки в bash". Фактически почти во всех написанных мною сценариях я использую одинарные скобки, за исключением случаев, когда мне нужно выполнить сопоставление с образцом. Я думаю, что нужно понимать разницу между [(то есть test) [[и использовать тот, который подходит для его нужд.
Вейцзюнь Чжоу

@ WeejunZhou умело разрабатывать, в каких случаях отдельные скобки лучше?
Юбер Гжесковяк

Это скорее личный вкус, я просто нахожу, что слишком смело говорить «Всегда используйте двойные квадратные скобки в bash». Но есть некоторые крайние случаи, которые я использовал. Одиночные скобки позволяют указать сам тест в var. В качестве упрощенного примера рассмотримif ....; then mytest='-gt'; else mytest='-eq'; fi; #several lines of code; if [ "$var1" "$mytest" "$var2" ]; then ...; fi
Вейцзюнь Чжоу

1
@WeijunZhou Ваш пример - сильный аргумент против одинарных квадратных скобок. Это делает код намного труднее для понимания и открывает окно, открытое для ошибок. Двойные скобки являются более строгими и поощряют более чистый код.
Юбер Гжесковяк

18

Давным-давно, когда все, что у нас было, было sh, булевы были обработаны, полагаясь на соглашение testпрограммы, которое testвозвращает ложное состояние завершения, если выполняется без каких-либо аргументов.

Это позволяет думать о переменной, которая не установлена ​​как ложная, и переменной, установленной в любое значение, как истинная. Сегодня testон встроен в Bash и обычно известен своим псевдонимом из одного символа [(или исполняемым файлом, который используется в оболочках, в которых его нет, как отмечает дольмен):

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 согласно странице руководства Bash, поэтому проблем с производительностью не должно быть. То же самое, например, с Dash. (/ bin / sh может просто символическая ссылка на / bin / dash). Для того, чтобы использовать исполняемый файл вы должны использовать полный путь , т.е. /usr/bin/\[.
Ярно

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

Буквально просто копирую мой ответ и ухудшаю его.
Quolonel Questions

@QuolonelQuestions Переменные Bash не типизированы , поэтому нет смысла говорить. declare and use boolean variablesМы можем просто, более чем одним способом, имитировать / предполагать, что переменная имеет тип . Я не видел нигде упомянутого в вашем ответе.
sjsam

10

Вместо того, чтобы притворяться логическим и оставлять ловушку для будущих читателей, почему бы просто не использовать лучшее значение, чем true и false?

Например:

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. Что касается кодов 0завершения программы (которые исторически использует bash), то это для положительного результата, trueа все остальное - отрицательный / ошибка или false.
Юбер Гжесковяк

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

Таким образом, простой способ установить переменный истинные ( с помощью этой методики синтаксиса) будет, var=1; наоборот var=''.

Ссылка:

-n = True, если длина строки var не равна нулю.

-z = True, если длина строки var равна нулю.


5

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

Математически булева алгебра напоминает целочисленную арифметику по модулю 2. Поэтому, если язык не предоставляет родной булев тип, наиболее естественным и эффективным решением является использование целых чисел. Это работает практически с любым языком. Например, в Bash вы можете сделать:

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

человек Баш :

((Выражение))

Выражение оценивается в соответствии с правилами, описанными ниже в разделе АРИФМЕТИЧЕСКАЯ ОЦЕНКА. Если значение выражения не равно нулю, возвращаемый статус равен 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]] = success, но также как [[0 -eq false]] = success, что неверно (хммм .... как насчет того, что встроенная функция является числовым значением?).

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

Существуют и другие перестановки условного выражения, которые также дадут неправильный вывод. По сути, все (кроме состояния ошибки, указанного выше), которое устанавливает переменную в числовое значение и сравнивает ее со встроенной функцией истина / ложь, или устанавливает переменную со встроенной истиной / ложью и сравнивает ее с числовым значением. Кроме того, все, что устанавливает переменную во встроенное значение true / false и выполняет сравнение, используя -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

Итак, теперь к тому, что работает. Используйте встроенные true / false для сравнения и оценки (как отметил Майк Хант, не заключайте их в кавычки). Затем используйте одинарный или двойной знак равенства (= или ==) и одинарные или двойные скобки ([] или [[]]). Лично мне нравится знак двойного равенства, потому что он напоминает мне о логических сравнениях в других языках программирования, и двойные кавычки только потому, что я люблю печатать. Итак, эти работы:

# 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встроенные модули не используются здесь (игнорировать то , что подсветка синтаксиса некоторых редакторов может означать), особенно в тех […]случаях , вы можете думать об этом как простая строка здесь (тот , который дается в качестве параметра в [команде).
февраля

У вас есть это сейчас.
Питер Мортенсен

4

Мои выводы и предложения немного отличаются от других постов. Я обнаружил, что могу использовать «булевы значения» в основном так же, как и на любом «обычном» языке, без предложенного «прыжка с обручем» ...

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

Если вы установите переменную на true, она впоследствии оценивается как «утвердительная» в условном выражении. Установите его false, и он оценивается как «отрицательный». Очень просто! Единственное предостережение, что неопределенная переменная также оценивается как истина ! Было бы неплохо, если бы это произошло наоборот (как это было бы в большинстве языков), но это хитрость - вам просто нужно явно инициализировать ваши логические значения как true или false .

Почему это работает так? Этот ответ в два раза. A) истина / ложь в оболочке действительно означает «нет ошибки» против «ошибки» (т. Е. 0 против чего-либо еще). Б) истина / ложь - это не значения, а операторы в сценариях оболочки! Что касается второй точки, выполнение trueили falseв строке само по себе устанавливает возвращаемое значение для блока, в котором вы находитесь, к этому значению, то falseесть является декларацией «обнаружена ошибка», где true «очищает» это. Использование его с присваиванием переменной «возвращает» это в переменную. 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

Вот реализация с короткими руками 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
Без скобок выражение не оценивается.
будет

@ да, это так. Вам не нужны (с).
phil294

1
@Blauhirn ... Привет, я основал свои комментарии на экспериментах с GNU Bash на компьютере Linux Mint / Ubuntu. Вы, вероятно, правы в теории () - не нужны. Мой единственный ответ - попробовать, кажется, это зависит от версии Bash, фактического выражения или контекста и тому подобного.
будет

1

Вот улучшение первоначального ответа miku, в котором рассматриваются опасения Денниса Уильямсона относительно случая, когда переменная не установлена:

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 для оболочки.
Гогаска

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 Тестирование возвращаемого значения функции отличается от тестирования переменной для целей сценария?
Джонраф

Ну, вопрос о переменных.
Ярно

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

-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
Вы должны объявить свои константы только для чтения. Также, пожалуйста, всегда используйте фигурные скобки при использовании переменных. Это соглашение, которое каждый должен придерживаться ИМХО. Большим недостатком этого решения является то, что вы не можете смешивать алгебраическое выражение с контрольными флагами или сравнениями строк.
Юбер Гжесковяк
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.