bash - сценария - сценарии linux




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

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

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

Поэтому ответ довольно ясен. В bash нет boolean variable . Тем не мение :

Используя оператор declare, мы можем ограничить присвоение значения переменным. [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 . Надеюсь, что цель понятна.

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

variable=$false

variable=$true

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

if [ $variable ]

if [ !$variable ]

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

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

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

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

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

    test "$var" = true && CodeIfTrue || CodeIfFalse
    
  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_a=true
      echo "$var_a"
      
       true
      

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

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

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

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

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

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

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

Баш действительно путает проблему с подобными [ , [[ , (( , $(( и т. Д.).

Все рассматриваются в кодовых пространствах друг друга. Я предполагаю, что это в основном историческое, где Башу приходилось иногда притворяться.

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

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
    

    что не так красиво.

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


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

Например:

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

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

temp1=true
temp2=false

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

Вот реализация короткой передачи, 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"

Давным-давно, когда все, что у нас было, было sh , booleans, где обрабатывается, полагаясь на соглашение test программы, где test возвращает статус ложного выхода, если он не имеет аргументов. Это позволяет думать о переменной, которая не задана как false, а переменная установлена ​​на любое значение как значение true. Сегодня тест встроен в 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

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


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

Код в ответе miku не имеет абсолютно никакого отношения к Bash builtin 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

Единственное отличие здесь состоит в том, что сравниваются четыре символа: 'y', 'e', ​​'a' и 'h' вместо 't', 'r', 'u' и 'e'. Вот и все. Нет попытки вызвать команду или встроенную именованную yeah , а также нет (на примере miku) всякая специальная обработка происходит, когда Bash анализирует токен true . Это просто строка и совершенно произвольная.

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

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 (успех), выполните предложение then if

Скобки:

  1. Разверните переменную $the_world_is_flat на строку "true" .
  2. Разберите теперь полностью расширенное условное выражение, которое имеет форму string1 = string2 . Оператор = оператор сравнения строк . Так...
  3. Сопоставьте строковое сравнение "true" и "true" .
  4. Да, две строки были одинаковыми, поэтому значение условного значения истинно.
  5. Выполните предложение then .

Код без скобок работает, потому что true команда возвращает код выхода 0, что указывает на успех. Код в скобках работает, потому что значение $the_world_is_flat идентично строковому $the_world_is_flat true в правой части = .

Просто, чтобы проехать домой, рассмотрите следующие два фрагмента кода:

Этот код (если выполняется с правами root) перезагрузит ваш компьютер:

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

Этот код просто печатает «Хорошая попытка». Команда reboot не вызывается.

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

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

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

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

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


Короче:

В bash нет булевых

Что такое bash, есть булевы выражения в терминах сравнения и условий. Тем не менее, то, что вы можете объявить и сравнить в bash, это строки и числа. Вот и все.

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

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

if true; then ...

по существу ...

if COMMAND; then ...

Условие истинно, когда команда возвращает код выхода 0.

Вы могли бы так же хорошо сделать это:

if which foo; then echo "program foo found"; fi

При использовании квадратных скобок или test команды вы полагаетесь на код выхода этой конструкции. Имейте в виду, что [ ] и [[ ]] также являются просто командами / встроенными, как и любые другие. Так ...

if [[ 1 == 1 ]]; then echo yes; 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". 

Я нашел существующие ответы запутанными.

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

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

snapshotEvents=true

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

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

snapshotEvents=false

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

Который также отлично работал.

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

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


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







sh