Почему `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 на уровне системного вызова. Это интересно. Является bash не проводка стандартного ввода в подоболочку?:(

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

РЕДАКТИРОВАТЬ:

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

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

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

РЕДАКТИРОВАТЬ 2: Я задал новый вопрос, который более полно объясняет контекст, который я описал чуть выше.

1 ответ

Решение

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

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

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

Из № 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 теперь является потерянной группой процессов, потому что "родитель каждого участника либо сам является членом группы, либо не является членом сеанса группы". (ПИД 906PPID это 903 а также 903 находится в другой сессии.)
  3. Когда процесс в потерянной группе фоновых процессов читает со своего управляющего терминала, read() потерпит неудачу с EIO,

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

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

Из № 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's waiting here so looks like it's 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. Но на этот раз пгрп 10803 не осиротевший, потому что его член PID 10803 (bash) родитель (PID) 10732, bash) находится в другом pgrp (PGID) 10732) и он находится в одном сеансе (SID 10732).

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

Заключение:

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

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

(cat <(cat))

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

Другие вопросы по тегам