Debian.pro

Про Debian


Что на самом деле делает if в bash/sh.

На днях я наткнулся в очередной раз на конструкцию вида program; var=$?; if [ $var != "1"] ; then ... ; fi. В этот момент я вспомнил про то, что давно уже (году в 2012-м, угу!) хотел написать статью о том, что именно происходит внутри if (кстати, и внутри while по сути). Ну просто для того, чтобы кидать эту ссылку в лицо тем, кто пишет странные вещи в if-ах. Вообще статья ни о чём, но в качестве ещё одного напоминания — почему бы и нет?

В общем, tl;dr:

if/elif проверяет только и только exit code вызываемой программы.
[ ... ] и [[ ... ]] являются всего лишь программой, которые чаще всего используются в if, а не частью синтаксиса if.
[ - это обертка для test (о чём однозначно говорится в man test), а [[ - bash-builtin-функция, умеющая чуть больше, чем test, но обратно совместимая с его синтаксисом.

К чему нас это приводит на практике?
Во-первых, [ и [[ можно использовать и без if-а, что бывает весьма полезно:

user@host:~$ [ $(date +%Y) -le "2020" ] && echo "year is not 2020 yet"
year is not 2020 yet

Хотя, чаще вы встретите что-то такое (типа присвоение дефолтного значения пустой переменной — да, для этого есть встроенная в bash конструкция, но всё же):

user@host:~$ [ -z "${var}" ] && var="foo"

Ну а во-вторых (что намного важнее) разные программы и функции можно вызывать прямо из if-а:

user@host:~$ if curl -so /dev/null https://google.com ; then echo "Google looks available"; fi
Google looks available
user@host:~$ if grep -qi intel /proc/cpuinfo ; then echo "Intel CPU here, i hope"; fi
Intel CPU here, i hope

Можно вызвать subshell для выполнения команды с пайпами и прочим (вообще subshell можно и для одной программы вызвать, но нафига?):

user@host:~$ if (dmesg | grep -qi error); then echo "Check your dmesg"; fi
Check your dmesg

(и да, я знаю, что можно просто погрепать /var/log/dmesg, но знаете как сложно примеры придумывать? Да и кто-то злой мог поменять путь до dmesg-лога).
Нужно учитывать, что длинная команда будет выполняться при каждом её вызове внутри if (тут никакой магии, но вдруг вы её ждёте):

user@host:~$ time (sleep 10; if sleep 10; then echo; fi)

real 0m20.009s
...

В любой момент можно добавить символ ! после if/elif. Тогда будет проверяться то, что код возврата программы НЕ равен нулю.
Заведем смешную функцию:

returner () {
    return $1
}

И повызываем её.
Для начала просто чтобы вы понимали, что функция делает:

user@host:~$ returner 123; echo "${?}"
123

И теперь с if-ом:

user@host:~$ if returner 0 ; then echo OK; fi
OK
user@host:~$ if ! returner 0 ; then echo OK; fi


user@host:~$ if returner 10 ; then echo OK; fi


user@host:~$ if ! returner 10 ; then echo OK; fi
OK

Ну и самое, наверное, важное, ради чего я всё-таки решил написать миллионную статью-копипасту. Знание всего этого позволяет сильно сэкономить буквы, когда у вас есть множество условий (особенно когда они часто меняются), но только 2 варианта действий. Или когда сложное условие хочется спрятать подальше от основного кода, чтобы повысить читаемость.
Мы всегда можем написать функцию, внутри которой будет происходить какая-то гребаная магия, а if-ом проверять только код возврата этой самой функции. Пример будет слегка… кхм… натянутым, но всё же (по смыслу мы внутри функции is_admin разрешаем что-то сделать пользователю внутри скрипта, если его username в списке или у него есть sudo):

#!/bin/bash
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 тоже проверяет именно код возврата.

Чем админить серверы с телефона? Termux (android ssh client).

На всякий случай напоминаю — на blackberry keyone/keytwo/K2 LE есть физическая клавиатура с кнопкой ctrl. Долгое время я думал о том, как было бы офигенно открывать консоль на телефоне и просто писать ssh host куда нужно. Более или менее что-то похожее было в Terminus, но доверять ему ключ/пароли не хотелось (а нечего было всякие облачные [...]

Вместо новогоднего нытья.

Каждый новый год я пишу пост с каким-нибудь нытьём о том, что «раньше была трава зеленее». Этот год был трудным, следующий… следующий будет, а там посмотрим. Поэтому вот вам лучше картинка: Ну и, собственно, вопрос. Тут вообще кто-то в живых остался? Писать что-то? Или нафиг?

Большой Мануал, часть 25: логгируем вызовы mail() из php.

Эта статья — часть Большого Мануала по настройке lamp-сервера на debian. Предыдущая часть цикла — Боремся с вирусней на сайтах. Следующая часть цикла — Not yet published Исторически большая часть вирусни для php занимается именно тем, что шлёт спам. Навскидку я бы оценил количество этой вирусни в половину от всей. Поэтому логгирование вызовов mail() сильно [...]

sslh + ipv6 + <censored> — чудесная идея бесплатно.

Статья обфусцирована, сами знаете почему. С начала прошлой недели у меня была идея-фикс. Поднять open<censored> сервер на территории матушки-России, подцепиться к нему по ipv6 с территории страны-агрессора, а v4-клиентами из RM -RF выходить наружу уже оттуда, откуда нужно. Ну то есть точка входа здесь, точка выхода там, а сами точки общаются исключительно по ipv6 (от [...]

Запускаем один NoVNC для доступа к нескольким VNC. Например, к консолям KVM-виртуалок.

Статью пишу «по памяти», воспроизвести вживую негде (да и лень). Если найдете ошибки и что-то не заработает — обязательно пишите. Я бы пока не рассматривал эту статью, как решение из разряда «copy-paste и работает». Но если вы понимаете происходящее ниже — то никакой проблемы запустить эту штуку нет, там всё банально. Ценность здесь, скорее, именно [...]

Новость одной строкой — 1.1.1.1, новый публичный резолвер.

На фоне клёвых первоапрельских новостей многие могли пропустить весьма важную новость. Компания Cloudflare совместно с APNIC запустила новый публичный резолвер на адресе 1.1.1.1 Что вдвойне приятно — с поддержкой dns over TLS (то бишь запросы до этого резолвера можно шифровать). Так же есть поддержка DNS over HTTPS, но его использовать может пока только FireFox в [...]

seedbox на коленке: deluged+deluge-web

Всем хороша связка rtorrent+rutorrent. Только настраивать её очень уж геморрно, да и уязвимость там нашли, помнится. Я давно уже использую deluge. Да и вообще мне интерфейс deluge-web больше нравится — выглядит посовременнее, сам он читаемый, да и вообще «не PHP и ладненько». Поэтому сегодня и расскажу, как настроить связку из deluged и родного вебинтерфейса к [...]

Пишем свой unit для SystemD

В некоторых следующих статьях помимо прочего придется запускать некоторые сервисы без «изкоробочного» init-скрипта или юнита. В 2018 году мне уже пришлось смириться с победой systemd и показывать в тех статьях init-скрипты я уже не буду. Но и рассказывать в каждой статье всю последовательность действий мне будет лень. Поэтому здесь я расскажу, что делать с unit-ом [...]