Какова лучшая практика для представления логического значения в сценарии оболочки?

15

Я знаю, что в них есть логические значения bash, но я никогда не видел, чтобы они использовались где-либо.

Я хочу написать обертку для некоторой часто просматриваемой информации на моей машине, например, вставлен / подключен ли этот конкретный USB-накопитель.

Какова была бы лучшая практика для достижения этого?

  • Строка?

    drive_xyz_available=true
  • Число (0 для истины, ≠ 0 для ложных)?

    drive_xyz_available=0    # evaluates to true
  • Функция?

    drive_xyz_available() { 
        if available_magic; then 
                return 0 
        else 
                return 1 
        fi
    }

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

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

Minix
источник
3
help true ; help false ; help exit
Костас
3
@Costas Не могли бы вы разработать?
Minix

Ответы:

2
bool(){ return "$((!${#1}))"; }

if bool "$var"
then : do true
else : do false

Просто установите переменную в любое значение, кроме not-null, чтобы вышеприведенное сработало, хотя [ -n "$var" ]оно будет короче, если не так очевидно.

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

Вышеприведенное возвращает логическое !notзначение len первого аргумента len - если аргумент содержит любое количество символов, кроме 0, он возвращает 0, в противном случае, если вообще нет символов, он возвращает 1. Это тот же самый тест, который вы можете выполнить [ -n "$var" ], в основном , но он просто оборачивает его в небольшую функцию с именем bool().

Обычно так работает переменная флага . Например:

[ -d "$dir" ] || dir=

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

for set in yes ''
do echo "${set:-unset or null}"
done

... который будет печатать ...

yes
unset or null

Конечно, также возможно сделать противоположное, :+но это может дать вам только заранее установленное значение по умолчанию или вообще ничего, в то время как приведенная выше форма может дать вам значение или значение по умолчанию.

И так относительно трех вариантов - любой может работать в зависимости от того, как вы решите его реализовать. Функция возвращает самопроверку, но, если по какой-либо причине требуется ее сохранение, ее необходимо поместить в переменную. Это зависит от варианта использования. Является ли логическое значение, которое вы хотите, чтобы один раз оценить тест и сделать тип? Если это так, сделайте функцию, иначе, возможно, понадобится любой из двух других.

mikeserv
источник
1
Я не думаю, что вы отвечаете на мой вопрос. Я не спрашиваю, как логические значения могут быть использованы в сценарии оболочки, но какой способ является наиболее распространенным и ожидаемым другим пользователем. Если мой вопрос неясен, я был бы рад отредактировать его. Также было бы неплохо краткое объяснение того, что делает ваш ответ. Спасибо.
Minix
@Minix лучше?
mikeserv
Я вообще присваиваю trueили falseпеременной, потом можешь сделатьif $variable; then ...
wurtel
@wurtel - который полагается на значение по умолчанию $IFS- и если значение переменной может содержать ввод пользователя любого рода, то также просто удача. Если значение не неизвестно для начала, тогда нет необходимости проверять его. Более безопасно это можно сделать if ${var:+":"} false; thenпри работе с булевыми нулевыми / ненулевыми значениями. Но это редко бывает полезнее, чем[ -n "$var" ] &&
mikeserv
Если я начну свой сценарий с, variable=falseа затем установлю его на true в соответствии с любым условием (точно так же, как если бы я использовал переменную в C), тогда у меня не возникнет никаких проблем, какова ваша одержимость изменением значений IFS, случайных значений переменных и т. Д. В любом случае ... .
wurtel
4

В bashкаждой переменной по сути есть строка (или массив, или функция, но давайте поговорим о обычных переменных здесь).

Условия анализируются на основе возвращаемых значений тестовых команд - возвращаемое значение не является переменной, это состояние выхода. Когда вы оцениваете if [ ... ]или, if [[ ]]или if grep somethingчто-то в этом роде, возвращаемое значение 0 (не строка 0, а состояние выхода 0 = успех) означает «истина», а остальные - «ложь» (то есть, совершенно противоположное тому, к чему вы привыкли в скомпилированных языках программирования, но поскольку есть один способ добиться успеха и много способов потерпеть неудачу, а ожидаемый результат выполнения обычно является успешным, 0 используется как наиболее распространенный результат по умолчанию, если ничего не происходит неправильно). Это очень полезно, потому что любой двоичный файл может быть использован в качестве теста - если он потерпит неудачу, это ложно, в противном случае это правда.

trueи falseпрограммы (обычно переопределяемые встроенными программами) - это просто полезные маленькие программы, которые ничего не делают - trueпреуспевают в том, что ничего не делают, и завершают работу с 0, в то время как falseпытаются ничего не делать и "терпят неудачу", завершаясь с 1. Звучит бессмысленно, но это очень удобно для сценариев.

Что касается того, как передать правду вокруг, это зависит от вас. Очень часто просто использовать «у» или «да» для истины и использования if [ x"$variable" = x"yes" ](добавляем фиктивную строку, xпотому что, если $variableполучится нулевая длина, это защищает от создания фиктивной команды, if [ = "yes" ]которая не разбирает). Также может быть полезно просто использовать пустую строку для false и использовать [ -z "$variable ]для проверки, является ли она нулевой длиной (или -nдля того, чтобы она была ненулевой).

В любом случае, довольно редко нужно передавать логические значения bash- гораздо чаще просто exitпри сбое или возвращать полезный результат (или ноль, если что-то идет не так, и проверка на пустую строку), и большинство случаев может проверка на отказ непосредственно из статата о выходе.


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

Кроме того, вам может даже не понадобиться returnзаявление. Если функция достаточно проста, вы можете использовать тот факт, что она просто возвращает статус последней выполненной команды в функции. Так что ваша функция может быть просто

drive_xyz_available() {
   [ -e /dev/disk/by-uuid/whatever ]
}

если вы проверяете наличие узла устройства (или grep, /proc/mountsчтобы проверить, смонтирован ли он?).

Орион
источник
Это очень хорошее резюме, спасибо, что нашли время, чтобы написать это. Можно ли из вашего последнего абзаца сделать вывод, что вы считаете вариант drive_xyz_available()наиболее распространенным?
Minix
Можете ли вы привести примеры для общего y = trueслучая? По моему опыту, большинство сценариев-оболочек проверяют любое ненулевое значение, чтобы считать его истинным - по крайней мере, когда речь идет о интерпретируемых переменных среды. В противном случае они вообще игнорируют ракушки.
mikeserv
3
Пустая строка для ifтеста не нужна, если переменная заключена в кавычки; if [ "$variable" = "yes" ]работает нормально, даже если переменная $ не установлена.
Даниэль Куллманн
-2

В основном, любое число, отличное от 0, является истинным, а 0 - ложным. Причина возврата значений в 0 для успешного завершения сценария или любого другого числа для выявления ошибок другого типа.

$? вернет код завершения предыдущей команды / исполняемого файла, равный 0 успешным, а любое другое число - ошибке, которая была возвращена.

Поэтому я бы использовал этот метод для истины / ложи. if (( ! $? ));then OK;else NOOK;fi

YoMismo
источник
Тогда я напишу один для последнего варианта. Спасибо.
Minix
5
Ненулевое число на самом деле ложно, а ноль истинно. Просто посмотрите на вывод true; echo $?и false; echo $?.
Руслан
@Ruslan. Вы проверили вывод моей команды? Я полагаю, что нет, иначе вы бы не заявили, что сделали. 0 - ложь, любое другое число - правда, причина отрицания !результата, чтобы оно было ИСТИНА. Результат команды равен 0, когда он завершился правильно, что не означает, что 0 является истиной. Слово trueможет быть 0, но 0 никогда не соответствует действительности, как ifпоказывает условие.
YoMismo
Это то же самое, говоря , что в C / C ++, 0это trueпотому , что if(!x){True();}else{False();}позвонит , True()когда x==0. Но правильной проверки не было бы !x, а скорее !!x.
Руслан
Вы ошибаетесь. Я не говорю о том, что и в каком порядке есть то, что вы пишете после. ifЯ просто констатирую, что здесь (в bash, или ksh, или tsh, или ....), как в C / C ++, 0 равно FALSE, любой другое число - TRUE, как вы можете прочитать в следующей ссылке, начальные реализации C не предоставили логический тип, определяемый как целые, где 0 - FALSE, а 1 - TRUE en.wikipedia.org/wiki/Boolean_data_type .
YoMismo