Почему `cat <(cat)` производит EIO?

У меня есть программа, которая читает из двух входных файлов одновременно. Я бы хотел, чтобы эта программа читалась со стандартного ввода. Я думал, что буду использовать что-то вроде этого:

$program1 <(cat) <($program2)

но я только что обнаружил, что

cat <(cat)

производит

....
mmap2(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb758e000
read(0, 0xb758f000, 131072)             = -1 EIO (Input/output error)
....
cat: -: Input/output error

и аналогично,

$ cat <(read -n 1)
bash: read: read error: 0: Input/output error

Итак... Linux не работает read на уровне syscall. Это интересно. Является ли bash не подключением stdin к подоболочке?: (

Есть ли решение? Мне нужно использовать подстановку процессов (формат ... <(...)), потому что $program1 (tail, кстати) ожидает файлы, и мне нужно выполнить некоторую предварительную обработку (с od) на стандартном входе, прежде чем я смогу передать ее tail - Я не могу просто указать /dev/stdin и др.

EDIT:

То, что я на самом деле хочу сделать, - это прочитать из файла (который будет писать другой процесс), а также читать со стандартного ввода, чтобы я мог принимать команды и т.д. Я надеялся, что смогу сделать

tail -f <(od -An -vtd1 -w1) <(cat fifo)

для чтения со стандартного ввода и FIFO одновременно и отбросить его в один поток stdout, который я мог бы выполнить через awk (или аналогичный). Я знаю, что я мог бы решить это тривиально на любом языке сценариев, но мне нравится учиться делать bash все: P

EDIT 2: я спросил новый вопрос, который более подробно объясняет контекст, описанный выше.

Ответ 1

1. Объясните, почему cat <(cat) производит EIO

(Я использую Debian Linux 8.7, Bash 4.4.12)

Давайте заменим <(cat) на долго работающий <(sleep), чтобы посмотреть, что происходит.

Из pty # 1:

$ echo $$
906
$ tty
/dev/pts/14
$ cat <(sleep 12345)

Перейдите к другому pty # 2:

$ ps t pts/14 j
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
   903    906    906    906 pts/14    29999 Ss       0   0:00 bash
   906  29998    906    906 pts/14    29999 S        0   0:00 bash
 29998  30000    906    906 pts/14    29999 S        0   0:00 sleep 12345
   906  29999  29999    906 pts/14    29999 S+       0   0:00 cat /dev/fd/63
$ ps p 903 j
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
     1    903    903    903 ?            -1 Ss       0   0:07 SCREEN -T linux -U
$

Позвольте мне объяснить это (согласно книге APUE, 2-е издание):

  1. TPGID, являющийся 29999, указывает, что cat (PID 29999) является группой процессов переднего плана, которая теперь управляет терминалом (pts/14). И sleep находится в группе фоновых процессов (PGID 906).
  2. Группа процессов 906 теперь является потерянной группой процессов, потому что "родитель каждого члена либо сам является членом группы, либо не является членом сеанса групп". (PID 906 PPID - 903, а 903 находится в другом сеансе.)
  3. Когда процесс в потерянной группе фоновых процессов читает со своего управляющего терминала, read() завершится ошибкой с EIO.

2. Объясните, почему cat <(cat) иногда работает (не совсем!)

Даниэль Война упомянул в комментарии, что cat <(cat) работает на OS X с Bash 3.2.57. Мне только что удалось воспроизвести это и на Linux с Bash 4.4.12.

Из pty # 1:

bash-4.4# echo $$
10732
bash-4.4# tty
/dev/pts/0
bash-4.4# cat <(cat)
cat: -: Input/output error
bash-4.4#
bash-4.4#
bash-4.4# bash --norc --noprofile  # start a new bash
bash-4.4# tac <(cat)
                      <-- It waiting here so looks like it working.

(Первая cat <(cat) ошибка с EIO была объяснена в первой части моего ответа.)

Перейдите к другому pty # 2:

bash-4.4# ps t pts/0 j
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
10527 10732 10732 10732 pts/0    10805 Ss       0   0:00 bash
10732 10803 10803 10732 pts/0    10805 S        0   0:00 bash --norc --noprofile
10803 10804 10803 10732 pts/0    10805 S        0   0:00 bash --norc --noprofile
10804 10806 10803 10732 pts/0    10805 T        0   0:00 cat
10803 10805 10805 10732 pts/0    10805 S+       0   0:00 tac /dev/fd/63
bash-4.4# ps p 10527 j
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
10526 10527 10527 10527 ?           -1 Ss       0   0:00 SCREEN -T dtterm -U
bash-4.4#

Давайте посмотрим, что происходит:

  1. TPGID, являющийся 10805, указывает, что tac (PID 10805) является группой процессов переднего плана, которая теперь управляет терминалом (pts/0). И cat (PID 10806) находится в группе фоновых процессов (PGID 10803).

  2. Но на этот раз pgrp 10803 не потерян, поскольку его родительский элемент PID 10803 (bash) (PID 10732, bash) находится в другом pgrp (PGID 10732) и в том же сеансе (SID 10732).

  3. Согласно книге APUE, SIGTTIN будет "сгенерирован драйвером терминала, когда процесс в (не потерянной) группе фоновых процессов попытается прочитать данные с его управляющего терминала". Поэтому, когда cat читает stdin, ему будет отправлено SIGTTIN, и по умолчанию этот сигнал остановит процесс. Вот почему столбец cat STAT был показан как T (остановлен) на выходе ps. Так как он остановил данные, которые мы вводим с клавиатуры, ему вообще не отправляются. Так что это выглядит как это работает, но это не совсем так.

Вывод:

Таким образом, различные варианты поведения (EIO и SIGTTIN) зависят от того, является ли текущий Bash лидером сеанса или нет. (В 1-й части моего ответа bash PID 906 является лидером сеанса, но bash PID 10803 во 2-й части не является лидером сеанса.)

Ответ 2

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

(cat <(cat))

Пожалуйста, найдите детали решения здесь:https://unix.stackexchange.com/a/244333/89706