![SIGPIPE и bash pipefail](https://rvso.com/image/231080/SIGPIPE%20%D0%B8%20bash%20pipefail.png)
Я пытаюсь лучше понять SIGPIPE в Linux.
Я провел этот эксперимент: { ls -al /tmp/ ; echo "$?" 1>&2 ; } | head
и он выводит эхо 141
, которое, как я понимаю, является кодом выхода, который присваивается процессам, которые завершаются с помощью SIGPIPE
Раньше я делал это много раз, не понимая нюансов, SIGPIPE
и я обычно запускаю с помощью set -eEuo pipefail
, поэтому я пытался понять, почему мой код не давал сбой все время из-за сломанных каналов... Поэтому я провел этот другой эксперимент: ( set -o pipefail; { ls -al /tmp/ ; echo "$?" 1>&2 ; } | head; )
и в тот раз я получил 0 в эхо... так что когда pipefail
включено, это означает, что SIGPIPE
оно подавлено? или я неправильно понимаю, что здесь происходит?
решение1
ls
может успеть записать весь свой вывод до head
выхода и разрыва конвейера, даже если вывод ls
длиннее, чем то, что head
печатается. Это происходит потому, что:
head
может прочитать больше, чем в конечном итоге напечатать,- и в любом случае для трубы есть буфер.
SIGPIPE запускается фактической записью в сломанный канал. Если ls
успеет записать весь свой вывод до head
выхода, то не будет никакого акта записи, который запускает SIGPIPE.
Или ls
может не успеть записать весь свой вывод перед head
выходом. Тогда он попытается записать больше и получит SIGPIPE.
Поскольку ls
и head
работают параллельно, я думаю, что в общем случае есть состояние гонки. Может случиться так, что какой-то конкретный вывод из ls
вызывает или не вызывает SIGPIPE, случайным образом.
Попробуйте yes
вместо ls …
:
{ yes ; echo "$?" 1>&2 ; } | head
или
( set -o pipefail; { yes ; echo "$?" 1>&2 ; } | head; )
yes
генерирует вывод, который не заканчивается сам по себе, поэтому после head
выхода всегда будет акт записи, который запускает SIGPIPE. Каждая из приведенных выше команд даст вам 141
наверняка.
Это не имеет никакого отношения к pipefail
.