В настоящее время у меня есть сценарий, который делает что-то вроде
./a | ./b | ./c
Я хочу изменить его так, чтобы при выходе любого из a, b или c с кодом ошибки я печатал сообщение об ошибке и останавливался вместо того, чтобы передавать плохой вывод вперед.
Каким будет самый простой способ сделать это?
shell
error-handling
pipe
hugomg
источник
источник
&&|
что означало бы «продолжать конвейер только в том случае, если предыдущая команда была успешной». Я полагаю, вы также могли бы иметь,|||
что означало бы «продолжить конвейер, если предыдущая команда не удалась» (и, возможно, передать сообщение об ошибке, как в Bash 4|&
).a
,b
,c
команды не выполняются последовательно , а параллельно. Другими словами, потоки данных последовательно отa
доc
, но фактическиa
,b
иc
команды запуска (примерно) в то же самое время.Ответы:
Если вы действительно не хотите, чтобы вторая команда продолжалась до тех пор, пока первая не станет успешной, вам, вероятно, придется использовать временные файлы. Вот простая версия:
Перенаправление «1> & 2» можно также обозначить как «> & 2»; однако старая версия оболочки MKS неправильно обрабатывала перенаправление ошибок без предшествующей «1», поэтому я использовал это недвусмысленное обозначение надежности на протяжении многих лет.
Это приводит к утечке файлов, если вы что-то прерываете. В защищенном от взрыва (более или менее) программировании оболочки используются:
В первой строке прерывания написано «выполнить команды»
rm -f $tmp.[12]; exit 1
при появлении любого из сигналов 1 SIGHUP, 2 SIGINT, 3 SIGQUIT, 13 SIGPIPE или 15 SIGTERM или 0 (при выходе из оболочки по любой причине). Если вы пишете сценарий оболочки, последней ловушке нужно только удалить ловушку на 0, которая является ловушкой выхода из оболочки (вы можете оставить другие сигналы на месте, поскольку процесс в любом случае завершится).В исходном конвейере 'c' может считывать данные из 'b' до завершения 'a' - обычно это желательно (например, это дает работу нескольким ядрам). Если «b» - это фаза «сортировки», то это не применимо - «b» должен увидеть весь свой ввод, прежде чем он сможет сгенерировать какой-либо из своих выходных данных.
Если вы хотите определить, какая команда не работает, вы можете использовать:
Это просто и симметрично - его легко расширить до 4-х или N-сегментного конвейера.
Простые эксперименты с set -e не помогли.
источник
mktemp
илиtempfile
.trap
помните, что пользователь всегда может послатьSIGKILL
процессу, чтобы немедленно завершить его, и в этом случае ваша ловушка не сработает. То же самое происходит, когда ваша система испытывает отключение электроэнергии. При создании временных файлов обязательно используйте,mktemp
потому что это поместит ваши файлы куда-нибудь, где они будут очищены после перезагрузки (обычно в/tmp
).В bash вы можете использовать
set -e
иset -o pipefail
в начале вашего файла. Последующая команда./a | ./b | ./c
завершится ошибкой при сбое любого из трех сценариев. Код возврата будет кодом возврата первого неудачного сценария.Обратите внимание, что
pipefail
это недоступно в стандартном sh .источник
set
(всего 4 разных версии) и посмотрите, как это повлияет на вывод последнегоecho
.Вы также можете проверить
${PIPESTATUS[]}
массив после полного выполнения, например, если вы запустите:Затем
${PIPESTATUS}
будет массив кодов ошибок каждой команды в конвейере, поэтому, если средняя команда не удалась,echo ${PIPESTATUS[@]}
будет содержать что-то вроде:и что-то вроде этого запускается после команды:
позволит вам проверить, что все команды в канале выполнены успешно.
источник
#!/bin/sh
, потому что, еслиsh
это не bash, он не будет работать. (Легко исправить, если не забыть использовать#!/bin/bash
вместо этого.)echo ${PIPESTATUS[@]} | grep -qE '^[0 ]+$'
- возвращает 0, если$PIPESTATUS[@]
содержит только 0 и пробелы (если все команды в канале выполнены успешно).command1 && command2 | command3
если какой-либо из них не работает, ваше решение вернет ненулевое значение.К сожалению, для ответа Джонатана требуются временные файлы, а для ответов Мишеля и Имрона требуется bash (хотя этот вопрос помечен как оболочка). Как уже указывалось другими, невозможно прервать конвейер до запуска более поздних процессов. Все процессы запускаются сразу и, таким образом, все запускаются до того, как можно будет сообщить о каких-либо ошибках. Но в заголовке вопроса также говорилось о кодах ошибок. Их можно получить и исследовать после завершения конвейера, чтобы выяснить, не завершился ли какой-либо из задействованных процессов.
Вот решение, которое ловит все ошибки в трубе, а не только ошибки последнего компонента. Это похоже на pipefail в bash, только более мощный в том смысле, что вы можете получить все коды ошибок.
Чтобы определить, не удалось ли что-нибудь,
echo
команда выводит стандартную ошибку в случае сбоя какой-либо команды. Затем объединенный стандартный вывод ошибок сохраняется$res
и исследуется позже. По этой же причине стандартная ошибка всех процессов перенаправляется на стандартный вывод. Вы также можете отправить этот вывод/dev/null
или оставить его как еще один индикатор того, что что-то пошло не так. Вы можете заменить последнее перенаправление на/dev/null
файл, если вам не нужно хранить вывод последней команды где угодно.Чтобы больше поиграть с этой конструкцией и убедить себя, что она действительно делает то, что должно, я заменил
./a
,./b
и./c
подоболочки, которые выполняютecho
,cat
иexit
. Вы можете использовать это, чтобы убедиться, что эта конструкция действительно перенаправляет весь вывод от одного процесса к другому и что коды ошибок записываются правильно.источник