Почему `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

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


person i336_    schedule 17.03.2017    source источник
comment
Интересно, потому что это работает на OS-X. Так что это не из bash, как я изначально подозревал. Это интересно.   -  person Daniel Voina    schedule 17.03.2017
comment
@DanielVoina: Спасибо за информацию, мне было очень интересно узнать о других платформах!   -  person i336_    schedule 17.03.2017
comment
Я не совсем понимаю вашу настоящую потребность, так как немного странно tail stdin. Надеюсь, мой ответ может дать вам некоторую подсказку.   -  person pynexj    schedule 17.03.2017
comment
Подобные вопросы в StackOverflow дают мне надежду на профессию программиста.   -  person Leo Izen    schedule 17.03.2017
comment
@whjm: Очень хороший момент. Я полностью описал контекст в новом вопросе и принял ваш ответ на этот, поскольку он канонически отвечает на него :)   -  person i336_    schedule 17.03.2017
comment
@DanielVoina - поведение не связано с ОС. Возможно, OSX использует другую (более старую?) версию bash, которая работает с pgrps по-другому (с ошибками?).   -  person pynexj    schedule 17.03.2017
comment
@whjm: GNU bash, версия 3.2.57(1)-выпуск (x86_64-apple-darwin16) является стандартной версией OS-X.   -  person Daniel Voina    schedule 17.03.2017
comment
@DanielVoina - не могли бы вы помочь попробовать ssh user@host на своем OSX, как я упоминал в своем другом ответе?   -  person pynexj    schedule 18.03.2017
comment
@whjm: в сеансе ssh поведение также некорректно, см. здесь: pastebin.com/BRQCDV2G   -  person Daniel Voina    schedule 18.03.2017


Ответы (2)


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)

Перейдите к другому участку #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 теперь является группой потерянных процессов, поскольку "родительский элемент каждого члена либо сам является членом группы, либо не является членом сеанса группы" . (PPID PID 906 равен 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's waiting here so looks like it's working.

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

Перейдите к другому участку #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 читает стандартный ввод, на него будет отправлено SIGTTIN, и по умолчанию этот сигнал будет останавливать процесс. Вот почему столбец STAT cat был показан как T (остановлен) в выводе ps. Поскольку он остановлен, данные, которые мы вводим с клавиатуры, вообще не отправляются на него. Так что кажется, что это работает, но на самом деле это не так.

Вывод:

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

person pynexj    schedule 17.03.2017
comment
Почему это происходит? Что такое 903 и в каком сеансе мы его находим? - person Emil Vikström; 17.03.2017
comment
Добавлена ​​информация о 903. Но здесь все, что нам нужно знать, это то, что это в другом сеансе. - person pynexj; 17.03.2017
comment
а) Удар головой; теперь я должен выяснить второй подход. b) Большое спасибо за эту замечательную информацию :) (PS: GNU sleep принимает недокументированный параметр infinity.) - person i336_; 17.03.2017
comment
Я никогда не знаю этого. Не так просто, как ввести большое число. :) - person pynexj; 17.03.2017
comment
Глупый вопрос, но почему мы не можем использовать /dev/stdin и объединить его с каналом? Что-то вроде some_filtering_cmd | real_cmd /dev/stdin <(other_cmd) | tail и т. д. - person Leo Izen; 17.03.2017
comment
@whjm: Совершенно верно! sleep 12345 и sleep 9d меньше нажатий клавиш :) - person i336_; 17.03.2017
comment
@LeoIzen: я спросил новый вопрос с полным контекстом и подробностями, к вашему сведению - person i336_; 17.03.2017

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

(cat <(cat))

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

person Johnny Wong    schedule 10.10.2019
comment
Спасибо за перекрестную ссылку. Я, конечно, никогда бы не нашел этот вопрос. - person i336_; 10.10.2019