Условный конвейер в bash (пользовательский скрипт)

Условный конвейер в bash (пользовательский скрипт)

Как мне передать команды по конвейеру и внутри скрипта решить, следует ли прерывать конвейер или нет?

Это похоже наэтот вопрос. Но вместо этого awkя хотел бы использовать свой собственный скрипт:

check_ip | update_bind9

check_ipнеобходимо найти внешний IP-адрес ( curl -s ifconfig.co) и сравнить его с IP-адресом, сохраненным в файле ( ~/.ip).

Вот главный вопрос:

Если IP изменился, check_ipто следует передать IP через канал. Если нет, то следует разорвать канал.

Это check_ipразрывает канал или update_bind9игнорирует вызов, если он не вызван с IP?

Я провел несколько тестов, назвав: check_ip | echo "ok".

Я пытался exit 0, exit 1, return true, return false. Пытался set -e. Безуспешно.

решение1

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

Итак, это вторая команда, которая должна обрабатывать пустой stdin.


Но я бы сказал, что трубы изначально не подходят для этих целей.

(Может быть, вы путаете |(pipe) с ||(boolean OR)? Последнееявляется(Часто используется как более короткая альтернатива if/then, например, x || yвызовет y только в случае неудачи x, и, аналогично, x && yвызовет y только в случае успеха x.)

В общем случае следует использовать блок if/then. Например, if check_ipсам возвращает 0 (успех), когда адреса разные, но 1 (неудача), когда они одинаковые, это будет выглядеть так:

if addr=$(check_ip); then
    echo "$addr" | update_bind9
fi

В качестве альтернативы, если update_bind9 может определить адрес самостоятельно (без чтения stdin), это будет выглядеть так:

if check_ip_needs_updating; then
    update_bind9
fi

решение2

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

ifneвыполняет следующую команду только в том случае, если стандартный ввод не пуст.

В вашем случае это будет как check_ip | ifne update_bind9.

Скорее всего, ifneне установлен в вашем Linux по умолчанию. В моем Debian 10 он есть в moreutilsпакете. Кто-то может подумать, что дополнительный инструмент совершенно излишен, поскольку мы можем сохранить вывод check_ipв переменной и условно использовать его повторно, как в упомянутом ответе:

if addr=$(check_ip); then
    echo "$addr" | update_bind9
fi

Условие может быть [ -n "$addr" ], что угодно; важно то, что мы используем переменную. Код, подобный приведенному выше, может решить вашу проблему, но в целом с ним связано как минимум три проблемы. Предположим command1 | command2вместо check_ip | update_bind9. Решение обобщается до:

if foo=$(command1); then
    echo "$foo" | command2
fi

А потом:

  1. foo=$(command1)удалит все конечные символы новой строки из вывода command1. Затем echo "$foo"(илиprintf … "$foo") добавит ровно один символ новой строки (или некоторое фиксированное их количество соответственно). По этой причине command2может не получиться точный вывод от command1.

  2. command1может выдавать вывод, содержащий символ(ы) NUL. Bash не может хранить NUL в переменной (большинство оболочек не могут; zsh может). Если появляется NUL, то command2не будет получать точный вывод из command1.

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

Использование файла вместо переменной решает все проблемы, кроме последней. command1 | ifne command2защищен от всех трех проблем.

Связанный контент