Почему `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-е издание):
TPGID
являющийся29999
указывает на то, чтоcat
(PID29999
) - это группа процессов переднего плана, которая теперь управляет терминалом (pts/14
). А такжеsleep
находится в группе фоновых процессов (PGID906
).- Группа процессов
906
теперь является потерянной группой процессов, потому что "родитель каждого участника либо сам является членом группы, либо не является членом сеанса группы". (ПИД906
PPID это903
а также903
находится в другой сессии.) - Когда процесс в потерянной группе фоновых процессов читает со своего управляющего терминала,
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#
Давайте посмотрим, что происходит:
TPGID
являющийся10805
указывает на то, чтоtac
(PID10805
) - это группа процессов переднего плана, которая теперь управляет терминалом (pts/0
). А такжеcat
(PID10806
) находится в группе фоновых процессов (PGID)10803
).Но на этот раз пгрп
10803
не осиротевший, потому что его член PID10803
(bash
) родитель (PID)10732
,bash
) находится в другом pgrp (PGID)10732
) и он находится в одном сеансе (SID10732
).Согласно книге APUE,
SIGTTIN
будет "сгенерирован драйвером терминала, когда процесс в (не потерянной) группе фоновых процессов попытается прочитать данные с его управляющего терминала". Так когдаcat
читает stdin,SIGTTIN
будет отправлен на него и по умолчанию этот сигнал остановит процесс. Вот почемуcat
"sSTAT
столбец был показан какT
(остановился) вps
выход. Поскольку он остановлен, данные, которые мы вводим с клавиатуры, вообще не отправляются на него. Так что, похоже, это работает, но это не совсем так.
Заключение:
Так что разное поведение (EIO
против SIGTTIN
) зависит от того, является ли текущий Bash лидером сеанса или нет. (В 1-й части моего ответа удар по PID 906
является лидером сессии, но удар PID 10803
во 2-й части не ведущий сессии.)
Принятый ответ объяснил, почему, но я увидел одно решение, которое может решить эту проблему. Это путем добавления в него дополнительных()
, Такие как:
(cat <(cat))
Подробности решения можно найти здесь:https://unix.stackexchange.com/a/244333/89706