Рассмотрим этот скрипт:
#!/bin/bash
main() {
cd /tmp
}
main > /dev/null
echo goodbye
Перед Баш запускает функцию main
, он должен перенаправить свой собственный стандартный вывод /dev/null
. Затем необходимо запустить функцию main
. Затем перед выполнением команды echo
необходимо восстановить свой стандартный вывод до того, что было до перенаправления.
Вот как это делается. Чтобы сделать перенаправление, он открывает /dev/null
, получив (скажем) файловый дескриптор 3. Затем он дублирует свой файловый дескриптор 1 (стандартный вывод) на первый доступный fd ≥ 10, получая (допустим) 10. Затем он дублирует fd 3 (открыть на /dev/null
) до Fd 1, и закрывает FD 3.
Позже, чтобы отменить переадресацию, он дублирует FD 10 (открытую на оригинальном стандартном выводе в Bash) на дескриптор 1, и закрывает Fd 10.
Хорошо , теперь вернемся к вашему сценарию. Все то же происходит, когда ваш скрипт говорит main >/dev/null
, поэтому, пока выполняется main
, fd 10 открыт на исходном стандартном выходе оболочки, который является записываемым концом трубы до cat
.
Когда ваш main
говорит do_sth >/dev/null &
, раковина вилки. Дочерняя оболочка наследует fd 10, поэтому как родительская, так и дочерняя оболочки имеют fd 10, открытых на трубе. (Обратите внимание, что оболочка знает, что ему не нужно, чтобы сохранить свой стандартный вывод здесь из-за &
делает его вилкой.)
Когда ребенок оболочки, в do_sth
, работает sleep
, он ветвится запустить sleep
и ждет sleep
для выхода. Таким образом, дочерняя оболочка висит вокруг в течение пяти секунд, и за это время она открывает fd 10 на записываемом конце трубы.
cat
процесс не читает EOF на читаемом конце трубы, пока все дескрипторы файлов на записываемый конце закрыты, что не происходит до тех пор, что выходит ребенок оболочки, который не происходит до тех пор, sleep
выходов.