Я попытался объявить логическую переменную в оболочке script, используя следующий синтаксис:
variable=$false
variable=$true
Это правильно? Кроме того, если бы я хотел обновить эту переменную, я бы использовал тот же синтаксис? Наконец, следующий синтаксис для использования булевых переменных в качестве правильных выражений:
if [ $variable ]
if [ !$variable ]
Пересмотренный ответ (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/questions/2953646/how-to-declare-and-use-boolean-variables-in-shell-script
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 builtin true
от 2 июня 2010 года относится только к исходному ответу, а не к пересмотренному.
if
оператор выполняет содержимое переменной , которая является Bash встроенной true
. Любая команда может быть установлена как значение переменной, и ее выходное значение будет оценено.
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) особенно непослушен, потому что он выполнит команду, содержащуюся в переменной (поэтому условие оценивает значение true для допустимых команд 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, поэтому никогда не будет выполняться вложенный оператор. Сюрприз!
Цитируя значение ("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 но ваш код будет более защитным.
man woman
все равно будет считаться допустимой командой, даже если такой man-страницы не существует.==
с [
или test
не является переносимым. Учитывая, что переносимость является единственным преимуществом [
/ test
имеет преимущество над [[
, придерживайтесь =
.
Похоже, что существует некоторая непонимание здесь bash builtin true
, а точнее, о том, как bash расширяет и интерпретирует выражения внутри скобок.
Код в ответе miku не имеет ничего общего с bash builtin true
, ни /bin/true
, ни любым другим вкусом команды true
. В этом случае true
является не чем иным, как простой символьной строкой, и вызов команды true
/builtin никогда не производится ни путем назначения переменной, ни путем оценки условного выражения.
Следующий код функционально идентичен коду в ответе 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
. Это просто строка и абсолютно произвольная.
Обновление (2/19/2014): После того, как вы ответите в ответ на miku, теперь я вижу, откуда происходит путаница. Ответ Miku использует отдельные скобки, но фрагмент кода, к которому он ссылается, не использует скобки. Это просто:
the_world_is_flat=true
if $the_world_is_flat; then
echo 'Be careful not to fall off!'
fi
Оба фрагмента кода будут вести себя одинаково, но скобки полностью меняют то, что происходит под капотом.
Здесь bash делает в каждом случае:
Без скобок:
$the_world_is_flat
в строку "true"
."true"
как команду.true
(встроенный или /bin/true
, в зависимости от версии bash).true
(всегда 0) с 0. Вспомните, что в большинстве оболочек код выхода 0 указывает на успех, а что-то еще указывает на сбой.if
then
Кронштейны:
$the_world_is_flat
в строку "true"
.string1 = string2
. Оператором =
является оператор сравнения строк bash. Так что..."true"
и "true"
.if
then
.Код без скобок работает, потому что команда true
возвращает код выхода 0, что указывает на успех. Код в скобках работает, потому что значение $the_world_is_flat
идентично строковому литералу true
в правой части =
.
Чтобы просто догнать точку, рассмотрим следующие два фрагмента кода:
Этот код (если он запущен с корневыми привилегиями) перезагрузит ваш компьютер:
var=reboot
if $var; then
echo 'Muahahaha! You are going down!'
fi
Этот код просто печатает "Хорошая попытка". Команда reboot не вызывается.
var=reboot
if [ $var ]; then
echo 'Nice try.'
fi
Обновление (4/14/2014) Чтобы ответить на вопрос в комментариях относительно разницы между =
и ==
: AFAIK, нет никакой разницы. Оператор ==
представляет собой bash -специфический синоним для =
, и, насколько я понял, они работают точно так же во всех контекстах. Обратите внимание, однако, что я специально говорю о операторах сравнения строк =
и ==
, используемых в тестах [ ]
или [[ ]]
. Я не предполагаю, что =
и ==
взаимозаменяемы всюду в bash. Например, вы, очевидно, не можете выполнять назначение переменной с помощью ==
, например var=="foo"
(технически вы можете это сделать, но значение var
будет "=foo"
, потому что bash не видит a ==
здесь, он видит оператор =
(присваивание), за которым следует буквальное значение ="foo"
, которое просто становится "=foo"
).
Кроме того, хотя =
и ==
взаимозаменяемы, вы должны иметь в виду, что работа этих тестов зависит от того, используете ли вы его внутри [ ]
или [[ ]]
, а также от того, операнды цитируются. Подробнее об этом можно узнать здесь: Расширенные bash Руководство по сценариям: 7.3 Другие операторы сравнения (прокрутите вниз до обсуждения =
и ==
).
$the_world_is_flat && echo "you are in flatland!"
такие как $the_world_is_flat && echo "you are in flatland!"
Используйте арифметические выражения.
#!/bin/bash
false=0
true=1
((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true
Вывод:
верно
не false
Давным-давно, когда все, что у нас было, было 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
Из-за цитирования соглашения script авторы предпочитают использовать составную команду [[
, которая имитирует 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
Этот материал может запутать, когда нужны пробелы, строки с нулевой длиной и нулевые переменные, а также когда ваш script должен работать с несколькими оболочками.
[
это не просто псевдоним внутри bash
. Этот псевдоним также существует в виде двоичного файла (или в виде ссылки, указывающей на него) и может использоваться с голым sh
. Проверьте ls -l /usr/bin/\[
. С bash
/ zsh
вы должны вместо этого использовать [[
это действительно чистый внутренний и намного более мощный.
[
и test
также является BASH SHELL BUILTIN COMMAND в соответствии со страницей руководства Bash, поэтому проблем с производительностью не должно быть. То же самое, например, с Dash. (/ bin / sh может просто символическая ссылка на / bin / dash). Чтобы использовать исполняемый файл, вы должны использовать полный путь, то есть /usr/bin/\[
.
Как объявить и использовать логические переменные в оболочке script?
В отличие от многих других языков программирования, 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
используется для явного указания, что переменные только для чтения. Надеюсь, что цель понятна.
declare -ir false=0 true=1
? В чем преимущество использования массива?
r
option & readonly
. Я бы сделал это так, как вы предложили в моих сценариях
Вместо того, чтобы подделывать логическое значение и оставлять ловушку для будущих читателей, почему бы просто не использовать лучшее значение, чем 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
0
приводит к false
и 1
к true
. Что касается кодов завершения программы (которые исторически использует bash), то это 0
для положительного результата или true
а все остальное - отрицательный / ошибка или false
.
Билл Паркер получает проголосовали за то, что его определения были отменены из обычного соглашения о коде. Обычно true определяется как 0, а false определяется как ненулевое значение. 1 будет работать для false, как и 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 -ef true]] = success, но также [[0 -eq false]] = успех, что неверно (hmmm.... как насчет того встроенного в числовое значение?).
#**** NOTE *** This gives wrong output *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then
Существуют и другие перестановки условного выражения, которые также дадут неверный вывод. В принципе, все (кроме указанного выше условия ошибки), которое устанавливает переменную в числовое значение и сравнивает ее с построением true/false или устанавливает переменную в значение true/false builtin и сравнивает ее с числовым значением. Кроме того, все, что устанавливает переменную в true/false builtin и делает сравнение с использованием -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
Итак, теперь к тому, что работает. Используйте правильные/ложные встроенные функции как для вашего сравнения, так и для ваших оценок (как отметил Майк Хант, не добавляйте их в кавычки). Затем используйте либо знак одиночного, либо двойного равенства (= или ==), либо одиночные или двойные скобки ([] или [[]]). Лично мне нравится знак double equals, потому что он напоминает мне о логических сравнениях на других языках программирования и двойных кавычках только потому, что мне нравится печатать. Итак, эти работы:
#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
Там у вас есть.
true
/ false
здесь не используются (игнорируйте, что может означать подсветка синтаксиса некоторых редакторов), особенно в случаях […]
вы можете рассматривать здесь как простую строку (такую, которая задается как параметр в [
команду).
Короче говоря:
Что bash имеет, является булевыми выражениями в терминах сравнения и условий. Тем не менее, то, что вы можете объявить и сравнить в bash, - это строки и числа. Что это.
Где бы вы ни находили true
или false
в bash, это либо строка, либо команда/встроенный, который используется только для кода выхода.
Этот синтаксис...
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 ...
Чтобы это стало понятным для будущих читателей, я бы рекомендовал всегда использовать кавычки вокруг 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}" != "true" ]]; then ... # creates impression of booleans. Better compare against "false" here
if [[ "${var}" -eq "true" ]]; then ... # `-eq` is for numbers and doesn't read as easy as `==`
T
и F
чтобы прояснить, что это не настоящие логические значения.
Доброе утро, нежные люди. Я нашел существующие альтернативы запутанными. Лично я просто хочу иметь что-то, что выглядит и работает как C. Это то, что сработало для меня.
#
snapshotEvents=true
:
if ($snapshotEvents); then
# do stuff
fi
и чтобы все были счастливы, я тестировал:
#
snapshotEvents=false
:
if !($snapshotEvents); then
# do else stuff
fi
Что тоже отлично работает.
$snapshotEvents
оценивает значение (содержимое) переменной. Вам не нужны скобки, я просто нахожу их полезными.
Ниже приведено улучшение исходного ответа @miku, в котором рассматриваются проблемы @dennis относительно случая, когда переменная не задана:
the_world_is_flat=true
# ...do something interesting...
if ${the_world_is_flat:-false} ; then
echo 'Be careful not to fall off!'
fi
Проверить, является ли переменная ложной:
if ! ${the_world_is_flat:-false} ; then
echo 'Be careful not to fall off!'
fi
О других случаях (неприятный контент в переменной), это проблема с любым внешним вводом, поданным в программу. Перед тем, как доверять ему, должен быть проверен любой внешний вход. Но эта проверка должна быть выполнена только один раз, когда этот ввод будет получен. Это не должно влиять на производительность программы, делая это при каждом использовании переменной, как предлагает @dennis.
Bash действительно смущает проблему с подобными [, [[, ((, $((и т.д. все шаги на кодовых пространствах друг друга). Я предполагаю, что это в основном исторический, где bash должен притворяться Иногда.
В большинстве случаев я могу просто выбрать метод и придерживаться его. В этом случае я склонен объявлять (желательно в общем файле библиотеки, который я могу включить в мои фактические сценарии)
TRUE=1;FALSE=0
Затем я могу использовать ((арифметический)) оператор компаратора для проверки...
testvar=$FALSE if [[ -d ${does_directory_exist} ]]; then testvar=$TRUE; fi if (( testvar == TRUE )); then # do stuff 'cos directory does exist . . . fi
testvar=$TRUE
...which is not so pretty.
Это не идеальное решение, но оно охватывает все случаи, в которых мне нужен такой тест... так что я доволен им.
true
иfalse
в контексте большинства приведенных ниже фрагментов - просто простые строки, а неbash built-ins
!!! Пожалуйста, прочитайте ответ Майка Холта ниже. (Это один из примеров, когда высоко оцененный и принятый ответ ИМХО сбивает с толку и затеняет проницательный контент в менее проголосовавших ответах)true
. Оказывается, первоначальный ответ Мику действительно называлtrue
встроенным, но пересмотренный ответ - нет. Это привело к тому, что указанные комментарии оказались неверными в отношении того, как работает код Мику. С тех пор ответ Мику был отредактирован для явного отображения как исходного, так и пересмотренного кода. Надеемся, что это ставит в тупик путаницу раз и навсегда.