Почему команда ниже не выходит? Вместо выхода цикл работает бесконечно.
В то время как я обнаружил это поведение с помощью более сложной настройки, простейшая форма команды сводится к следующему.
Не выходит:
while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
Там нет опечаток выше. Каждый '|' это труба. «Выход 1» означает другой процесс, который запущен и завершен.
Я ожидаю, что «выход 1» вызовет SIGPIPE в цикле while (запись в канал без читателя) и разрыв цикла. Но цикл продолжает работать.
Почему команда не останавливается?
linux
command-line
bash
shell
bash-scripting
stephen.z
источник
источник
Ответы:
Это связано с выбором в реализации.
Выполнение одного и того же сценария в Solaris с
ksh93
приводит к другому поведению:Проблема вызывает внутренний конвейер, без которого цикл завершается независимо от оболочки / ОС:
cat
получает сигнал SIGPIPE под bash, но оболочка все равно повторяет цикл.Документация Bash гласит:
Кш документация гласит:
POSIX заявляет:
источник
echo
игнорирует SIGPIPE. Вы также можете воспроизвести проблему, используяenv echo
вместоecho
(для принудительного использования фактическогоecho
двоичного файла). (Сравните также вывод{ echo hi; echo $? >&2; } | exit 1
и{ env echo hi; echo $? >&2; } | exit 1
.)Эта проблема беспокоила меня годами. Спасибо jilliagre за толчок в правильном направлении.
Немного переформулировав вопрос, на моем linux box это выходит как положено:
Но если я добавлю канал, он не выйдет, как ожидалось:
Это расстраивало меня годами. Рассматривая ответ, написанный jilliagre, я придумал это замечательное исправление:
QED ...
Ну, не совсем. Вот что-то более сложное:
Это не работает правильно. Я добавил,
|| exit
так что он знает, как завершить работу рано, но самый первыйecho
не совпадает,grep
так что цикл завершается сразу. В этом случае вы действительно не заинтересованы в статусе выходаgrep
. Мой обходной путь - добавить еще одинcat
. Итак, вот надуманный сценарий под названием «десятки»:Это правильно завершается при запуске как
tens | head
. Слава Богу.источник