it-swarm.xyz

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

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

variable=$false

variable=$true

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

if [ $variable ]

if [ !$variable ]
728
hassaanm

Исправленный ответ (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

From: Использование логических переменных в Bash

Причина, по которой первоначальный ответ включен сюда, заключается в том, что комментарии до пересмотра 12 февраля 2014 года относятся только к первоначальному ответу, и многие комментарии неверны, когда связаны с пересмотренным ответом. Например, комментарий Денниса Уильямсона about bash builtin true 2 июня 2010 г. относится только к первоначальному ответу, а не к пересмотренному.

900
miku

TL; DR

bool=true

if [ "$bool" = true ]

Проблемы с ответом Мику ( оригинал )

Я не рекомендую принятый ответ1, Его синтаксис хорош, но у него есть некоторые недостатки.

Скажем, у нас есть следующее условие.

if $var; then
  echo 'Muahahaha!'
fi

В следующих случаях2, это условие оценивается как true и выполняет вложенную команду.

# 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!

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

Заключение в кавычки значения ("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. Если вы заботитесь о длине, первая рекомендация самая короткая.
657
Dennis

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

Код в ответ Мику не имеет абсолютно никакого отношения ни к встроенной в 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

Разница only в том, что сравниваемые четыре символа - это «y», «e», «a» и «h» вместо «t», «r», «u» и «e» , Вот и все. Не было предпринято никакой попытки вызвать команду или встроенную функцию с именем yeah, и при этом (в примере miku) не происходит какой-либо специальной обработки, когда Bash анализирует токен true. Это просто строка, причем совершенно произвольная.

Update (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

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

Однако обратите внимание, что я специально говорю об операторах сравнения строк = и ==, используемых в тестах [ ] или [[ ]]. Я не предполагаю, что = и == взаимозаменяемы везде в bash.

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

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

132
Mike Holt

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

#!/bin/bash

false=0
true=1

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

Результат:

правда
не ложь

42
Quolonel Questions

Короче:

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

У 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. Can be used for strict checking of dangerous operations. This condition is false for anything but the literal string "true". 
20
Hubert Grzeskowiak

Давным-давно, когда все, что у нас было, было 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

Этот материал может сбить с толку когда пробелы, строки нулевой длины и нулевые переменные необходимы, а также когда ваш скрипт должен работать с несколькими оболочками.

14
Hbar

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

В отличие от многих других языков программирования, Bash не разделяет свои переменные по типу. [1]

Так что ответ довольно ясен. В bash нет boolean variable. Однако:

Используя заявление объявления, мы можем ограничить присвоение значения переменными . [2]

#!/bin/bash
declare -ir BOOL=(0 1) #remember BOOL can't be unset till this Shell terminate
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 используется, чтобы явно указать, что переменные являются readonly. Надеюсь, цель ясна.

11
sjsam

Вместо того, чтобы выдавать логическое значение и оставлять ловушку для будущих читателей, почему бы просто не использовать лучшее значение, чем 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
6
Pyrolistical

Биллу Паркеру отказывают в голосовании , потому что его определения противоположны обычному соглашению о коде. Обычно 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

Там у вас есть это.

3
Randyman99

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

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

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

человек Баш :

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

Выражение оценивается в соответствии с правилами, описанными ниже в разделе АРИФМЕТИЧЕСКАЯ ОЦЕНКА. Если значение выражения не равно нулю, возвращаемое состояние равно 0; в противном случае возвращаемый статус равен 1. Это в точности эквивалентно пустому выражению. 

2
Cyker

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

Я здесь скучаю по ключевому моменту - мобильности. Вот почему мой заголовок содержит POSIX сам по себе.

По сути, все проголосовавшие ответы верны, за исключением того, что они слишком специфичны BASH .

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


  1. Квадратные скобки [ и ], как в [ "$var" = true ], не нужны, их можно опустить и напрямую использовать команду test:

    test "$var" = true && CodeIfTrue || CodeIfFalse
    
  2. Представьте, что эти слова true и false означают для Shell, проверьте сами:

    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_a=true
      echo "$var_a"
      
       true
      

      вы можете создать противоположное значение с помощью:

      var_a=$((1 - $var_a))
      echo "$var_a"
      
      1
      

      Как вы сами видите, Shell печатает строку true при первом ее использовании, но с тех пор все работает через число 0 или 1 соответственно.


Наконец, что вы должны делать со всей этой информацией

  • Первой хорошей привычкой будет назначение 0 вместо true; 1 вместо false.

  • Вторая хорошая привычка - проверять, равна ли переменная нулю:

    test "$var" -eq 0 && CodeIfTrue || CodeIfFalse
    
2
Vlastimil

Вот реализация короткого 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
llundin

Вот простой пример, который работает для меня:

temp1=true
temp2=false

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

Вот улучшение оригинального ответа 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

Что касается других случаев с неприятным содержимым в переменной, это проблема с любым внешним вводом, подаваемым в программу.

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

Это не должно влиять на производительность программы, делая это при каждом использовании переменной, как предлагает Деннис Уильямсон .

0
dolmen

Я нашел, что существующие ответы сбивают с толку.

Лично я просто хочу иметь что-то, что выглядит и работает как C.

Этот фрагмент работает много раз в день на производстве:

snapshotEvents=true

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

и чтобы все были счастливы, я проверил:

snapshotEvents=false

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

Который также работал нормально.

$snapshotEvents оценивает содержимое значения переменной. Итак, вам нужен $.

Тебе не нужны скобки, я просто нахожу их полезными.

0
will

Мои выводы и предложения немного отличаются от других постов. Я обнаружил, что могу использовать истину/ложь в основном так же, как и на любом «обычном» языке, без предложенного «прыжка с обручем» ... Нет необходимости в [] или явном сравнении строк ... Я пробовал несколько дистрибутивов Linux, я тестировал bash, dash и busybox. Я бегал с ней и без нее ... Результаты всегда были одинаковыми. Я не уверен, о чем идет речь в оригинальных топ-постах. Может быть, времена изменились, и это все, что нужно сделать?

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

Смотрите пример 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
0
BuvinJ