На днях я наткнулся в очередной раз на конструкцию вида program; var=$?; if [ $var != "1"] ; then ... ; fi
. В этот момент я вспомнил про то, что давно уже (году в 2012-м, угу!) хотел написать статью о том, что именно происходит внутри if (кстати, и внутри while по сути). Ну просто для того, чтобы кидать эту ссылку в лицо тем, кто пишет странные вещи в if-ах. Вообще статья ни о чём, но в качестве ещё одного напоминания — почему бы и нет?
В общем, tl;dr:
[ ... ] и [[ ... ]] являются всего лишь программой, которые чаще всего используются в if, а не частью синтаксиса if.
[ - это обертка для test (о чём однозначно говорится в man test), а [[ - bash-builtin-функция, умеющая чуть больше, чем test, но обратно совместимая с его синтаксисом.
К чему нас это приводит на практике?
Во-первых, [ и [[ можно использовать и без if-а, что бывает весьма полезно:
year is not 2020 yet
Хотя, чаще вы встретите что-то такое (типа присвоение дефолтного значения пустой переменной — да, для этого есть встроенная в bash конструкция, но всё же):
Ну а во-вторых (что намного важнее) разные программы и функции можно вызывать прямо из if-а:
Google looks available
Intel CPU here, i hope
Можно вызвать subshell для выполнения команды с пайпами и прочим (вообще subshell можно и для одной программы вызвать, но нафига?):
Check your dmesg
(и да, я знаю, что можно просто погрепать /var/log/dmesg, но знаете как сложно примеры придумывать? Да и кто-то злой мог поменять путь до dmesg-лога).
Нужно учитывать, что длинная команда будет выполняться при каждом её вызове внутри if (тут никакой магии, но вдруг вы её ждёте):
real 0m20.009s
...
В любой момент можно добавить символ ! после if/elif. Тогда будет проверяться то, что код возврата программы НЕ равен нулю.
Заведем смешную функцию:
return $1
}
И повызываем её.
Для начала просто чтобы вы понимали, что функция делает:
123
И теперь с if-ом:
OK
OK
Ну и самое, наверное, важное, ради чего я всё-таки решил написать миллионную статью-копипасту. Знание всего этого позволяет сильно сэкономить буквы, когда у вас есть множество условий (особенно когда они часто меняются), но только 2 варианта действий. Или когда сложное условие хочется спрятать подальше от основного кода, чтобы повысить читаемость.
Мы всегда можем написать функцию, внутри которой будет происходить какая-то гребаная магия, а if-ом проверять только код возврата этой самой функции. Пример будет слегка… кхм… натянутым, но всё же (по смыслу мы внутри функции is_admin разрешаем что-то сделать пользователю внутри скрипта, если его username в списке или у него есть sudo):
is_admin () {
# function returns 0, if username from $1 is admin
# and returns 1, if not
_username="${1}"
case ${_username} in
foo)
return 0
;;
bar)
return 1
;;
*)
if grep -q sudo groups ${_username}; then
return 0
else
return 1
fi
;;
esac
}
if ! is_admin $(whoami); then
echo "change user before do that"
else
echo "doing something"
fi
Вот. И да — я сознательно не писал ничего про всякие if-elif-else, это вроде и так все помнят. Само собой, elif тоже проверяет именно код возврата.
|| Да и кто-то злой мог поменять путь до dmesg-лога
Я бы объяснил это иначе.
Привязываясь к путям до файла, мы прибиваем гвоздями скрипт к текущему дистру.
Используя спец.команду, имеем кроссплатформенность, хотя и условную — команда все таки быть должна, но где лежит файл уже неважно.