Может ли следующая программа в конвейере увидеть код завершения предыдущей программы?

8

Я хотел бы сделать конвейер Bash-скриптов, как это

prog1 | prog2

так что prog2 может видеть код выхода prog1 и действовать по-разному в зависимости от этой информации.

Это возможно?

Дан
источник
Не могли бы вы разработать акт по-другому, часть вашего вопроса?
devnull
Вы имеете ограниченный контроль над тем, как далеко prog2продвинулись при prog1выходе из-за внутренней буферизации, используемой для реализации канала, и как prog1и prog2по расписанию.
chepner
Также посмотрите на [В каком порядке запускаются команды по каналам?] (Unix.stackexchange.com/q/37508)
DK Bose

Ответы:

4

Общий ответ - нет. Это возможно для prog2выхода prog1даже до запуска (очевидно, что это не может произойти, если на prog2самом деле читает какой-то ввод, что вы ожидаете, если он будет использовать его в конвейере). Это определенно возможно для prog2выхода раньше prog1; это происходит, например, когда prog2поисковая программа завершает работу, как только находит совпадение, и в этом случае, prog1возможно, еще не закончены все данные.

Не существует прямого способа prog2получить статус выхода prog1или даже узнать, что prog1он вышел. Все, что prog2может знать, - это то, что prog1закрыл свой конец трубы, что он может сделать, не умирая.

Если вы хотите получить статус выхода prog1из prog2, есть два распространенных метода: вы можете записать его в файл или отправить через канал. Отправка выходного статуса в качестве последней строки переданных данных возможна. Вы должны следить за тем, чтобы не обрабатывать последнюю строку, пока не узнаете, что это последняя строка, т.е. пока вы не попытаетесь прочитать следующую строку.

{ prog1; echo $?; } | 

Вот пример, где правая часть представляет собой текстовый фильтр, который окрашивает каждую строку, содержащую слово «ошибка», в красный цвет. Если левая сторона выходит из строя, правая сторона выходит с тем же статусом.

{ prog1; echo $?; } | awk '
    NR != 1 {
        if (line ~ /[Ee][Rr][Rr][Oo][Rr]/) print "\033[31m" line "\033[0m";
        else print line;
    }
    {line = $0}
    END {exit($0)}
'
Жиль "ТАК - перестань быть злым"
источник
Я пытался, { command; echo ${PIPESTATUS[@]}; } | sort | ...чтобы статус выхода был первым в потоке. Это все очень интересно!
@lightÉ Это будет работать только в том случае, если ${PIPESTATUS[@]}отсортировано до чего-либо еще в выводе command. Если вы commandраспечатываете набор чисел или если он может распечатать произвольный текст, у вас возникнут проблемы: вы не сможете отличить его вывод от строки состояния.
Жиль "ТАК - перестань быть злым"
Спасибо, в самом деле, он только успешно сортирует статус успеха наверх, если команда не содержит 0 в своем выводе lol. Tgif / с.
2

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

Для примера программы «просто продолжай», grepсервер будет, как и раньше tail -f /var/log/some_log_file. Использование sortв конвейере вызывает «остановку», так как sortбудет собирать входные данные, пока труба перед ним не закроется. Использование xargsдобавляет еще одну сложность: программы запускаются xargs(может начинаться много раз) частью конвейера или нет?

Брюс Эдигер
источник
-1 потому что ты голосовал за то, что за неправильный ответ
Ctrl-Alt-Delor
@ Ричард А? Ответ Брюса правильный (хотя второй абзац немного сбивает с толку).
Жиль "ТАК - перестань быть злым"
1

Ответ: не напрямую.

@terdon проиллюстрировал, что код выхода предыдущей команды в конвейере должен быть отправлен в качестве явного параметра для следующей команды.

Помните, что канал - это просто отображение STDOUT предыдущей команды в STDIN следующей команды; коды выхода не выводятся в STDOUT (или STDERR).

pepoluan
источник
1
-1 потому что вы проголосовали за то, что цитировали неправильный ответ, и не сказали намного больше.
ctrl-alt-delor
@ Richard достаточно справедливо ... Я должен был перепроверить ... вот что случилось, если я
заставлю
1

Весь процесс в конвейере запускается перед любым выходом. Следовательно, prog2может потребоваться получить эту информацию после того, как она началась, она также должна будет задержать обработку до тех пор, пока она не выйдет prog1, это может остановить конвейер. Кажется, есть фундаментальные проблемы в выполнении того, что вы просите, а не ограничения ОС.

Вам, вероятно, нужно рассмотреть временный файл или поместить результат в переменную.

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

tmp=$(prog1)
if test "z$PIPESTATUS" == "z0"
then
   
else
   
fi
Ctrl-Alt-Делор
источник
В твоих рассуждениях есть пробел. prog2запускается раньше, чем prog1завершается в целом, но может быть способ получить состояние вывода prog1во время его работы.
Жиль "ТАК - перестань быть злым"
0

Чтобы закончить ответ Жиля ,

(prog1; echo $? > /tmp/prog1.status) | prog2

это подход.  prog2может либо

  • прочитать стандартный ввод до конца, а затем прочитать /tmp/prog1.statusили
  • /tmp/prog1.statusпериодически проверяйте наличие при чтении стандартного ввода.
Скотт
источник