Буферизация строк в bash coproc

Я пытаюсь использовать bash coproc, и я Столкнулся с трудностями, скорее всего с буферизацией. У меня есть сложная команда, которая принимает линейный ввод со стандартного ввода и выводит строку на стандартный вывод для каждой строки ввода. В командной строке эта команда отлично работает для каждой строки, но когда я помещаю ее в coproc и читаю из ${COPROC[0]} FD, чтение блокируется.

Я могу воссоздать это поведение с помощью paste, но не с cat. Я ожидаю, что paste и cat сделают то же самое, если не будут переданы параметры. Это происходит при запуске непосредственно в командной строке:

$ cat
Hello World!<RETURN>
Hello World!^D
$ paste
Hello World!<RETURN>
Hello World!^D
$ 

(RETURN и ^D добавлены для иллюстрации)

Но когда я помещаю их в coproc, они ведут себя по-другому - cat строго буферизуется строкой, тогда как paste, похоже, работает с гораздо большим буфером:

$ coproc cat
$ echo 'Hello world!' >&${COPROC[1]}
$ read -ru ${COPROC[0]} line; echo $line
Hello world!
$ kill $COPROC_PID
[3]+  Terminated              coproc COPROC cat
$ 
$ coproc paste
[3] 42657
$ echo 'Hello world!' >&${COPROC[1]}
$ read -ru ${COPROC[0]} line; echo $line
#### read blocks here until ^C ####

Я думаю, причина этого в том, что paste настраивает свой режим буферизации в зависимости от того, к чему он подключен, тогда как cat всегда находится в режиме линейной буферизации.

Есть ли способ заставить paste (или другую общую команду) буферизовать строку в coproc?


Немного поэкспериментировав, я обнаружил, что могу воссоздать аналогичное поведение без coprocs и вместо этого просто соединяет cat и paste:

$ cat | cat
Hello World!<RETURN>
Hello World!^D
$ cat | paste
Hello World!<RETURN>
Hello World!^D
$ paste | cat
Hello World!<RETURN>
#### command blocks here until ^C ####

(RETURN и ^D добавлены для иллюстрации)

  • Сначала мы соединяем cat с cat и получаем буферизацию строк на всем пути.
  • Затем мы передаем cat в paste, а также получаем буферизацию строк на всем пути.
  • Наконец, мы передаем paste в cat и не получаем буферизацию строк.

Похоже, это указывает на то, что paste будет строчно буферизовать свой стандартный вывод в интерактивном режиме, но в противном случае он будет использовать гораздо больший буфер.


person Digital Trauma    schedule 07.01.2014    source источник


Ответы (1)


Один из способов принудительной буферизации строк — использовать инструмент stdbuf coreutils, если он доступен:

stdbuf позволяет изменять операции буферизации из трех стандартных потоков ввода/вывода, связанных с программой.

Для случая coproc:

$ coproc stdbuf -oL paste
[3] 42751
$ echo 'Hello world!' >&${COPROC[1]}
$ read -ru ${COPROC[0]} line; echo $line
Hello world!
$ kill $COPROC_PID
[3]+  Terminated              coproc COPROC stdbuf -oL paste
$ 

Для простой трубы от paste до cat случая:

$ stdbuf -oL paste | cat
Hello World!<RETURN>
Hello World!^D
$ 
person Digital Trauma    schedule 07.01.2014