Bash Анонимные трубки

При разработке цепочки команд для выполнения определенной задачи я столкнулся с проблемой, что анонимные трубы ведут себя не так, как ожидалось. Поскольку исходная команда, которую я запускаю, слишком сложна для объяснения здесь, я создал пример, который показывает проблему (я знаю, что все эти команды в основном ничего не делают). Кроме того, я использую pv, чтобы показать, действительно ли данные копируются из ввода в вывод.

cat /dev/zero | pv > /dev/null

Это работает так, как ожидалось. (копировать данные из /dev/zero в/dev/null)

cat /dev/zero | tee /dev/null | pv > /dev/null

Это также работает как ожидалось (дублируйте данные и отправляйте обе копии в /dev/null )

cat /dev/zero | tee >(pv -c > /dev/null) | pv -c > /dev/null

Эта команда работает только частично. Пока копия из STDIN в STDOUT все еще работает, (один pv покажет прогресс на короткое время), вся команда задерживается анонимным трубой, который ничего не получает и, следовательно, тройники, поскольку один из выходов не может быть записан в (Я проверил это, разрешив ему писать в файлы вместо /dev/null ).

Если у кого-то есть идея, почему это не работает (как ожидалось?) в bash, я был бы рад за помощь.

PS: Если я использую zsh вместо bash, команда выполняется так, как ожидалось. К сожалению, система, в которой нужно запустить, не имеет zsh, и мне не удастся запустить zsh в эту систему.

Ответ 1

Вы используете <( ... ) для замены процесса, процесс, выполняющийся внутри, не имеет управляющего терминала. Но pv всегда показывает свои результаты терминалу; если его нет, он будет остановлен.

Если вы выполняете свой код и, пока он работает, выполните ps axf, вы увидите что-то вроде этого:

23412 pts/16   S      0:00  \_ bash
24255 pts/16   S+     0:00      \_ cat /dev/zero
24256 pts/16   S+     0:00      \_ tee /dev/fd/63
24258 pts/16   S      0:00      |   \_ bash
24259 pts/16   T      0:00      |       \_ pv -c
24257 pts/16   S+     0:00      \_ pv -c

... который сообщает вам, что pv -c, выполняемый внутри подстановки процесса (один ниже второго bash), находится в состоянии T, остановлен. Он ожидает наличия управляющего терминала для запуска. У него его нет, поэтому он остановится навсегда, а bash в конечном итоге прекратит отправку данных в этот канал.