перенаправление stdin _and_ stdout в канал - PullRequest
3 голосов
/ 23 июня 2011

Я хотел бы запустить программу "A", чтобы ее выходные данные были переданы на вход другой программы "B", а также stdin - на вход "B". Если программа «А» закроется, я бы хотел, чтобы «Б» продолжил работу.

Я могу легко перенаправить выход A на вход B:
./a | ./b

И я могу объединить stderr в вывод, если захочу:
./a 2> & 1 | ./b

Но я не могу понять, как объединить стандартный вывод в вывод. Мое предположение будет:
./a 0> & 1 | ./b
но это не работает.

Вот тест, который не требует от нас переписывания каких-либо тестовых программ:

$ echo ls 0>&1 | /bin/sh -i
$ a  b  info.txt
$
/bin/sh: Cannot set tty process group (No such process)

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

Ответы [ 6 ]

3 голосов
/ 23 июня 2011

Этого нельзя сделать без написания вспомогательной программы.

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

Вам потребуется написать «вспомогательную» программу, которая контролирует два файловых дескриптора (скажем, 0 и 3), чтобы считывать их и «объединять» их.Было бы достаточно простого цикла select или poll, и вы могли бы написать его на большинстве языков сценариев, но не в оболочке, я не думаю.

Затем вы можете использовать перенаправление оболочки для подачивывод программы в дескриптор 3 «помощника».

Поскольку то, что вы хотите, в основном противоположно «tee», я могу назвать его «eet» ...

[edit]

Если бы вы только могли запустить "кот" в фоновом режиме ...

Но это не удастся, потому что фоновые процессы с управляющим терминалом не могут читать из стандартного ввода.Так что, если бы вы могли просто отсоединить "cat" от управляющего терминала и запустить его в фоновом режиме ...

В Linux "setsid cat" должен это сделать примерно.Но (а) я не мог заставить его работать очень хорошо, и (б) у меня действительно нет времени на это сегодня, и (в) это все равно нестандартно.

Я бы просто написал вспомогательную программу.

[edit 2]

ОК, похоже, это работает:

{ seq 5 ; sleep 2 ; seq 5 ; } | /bin/bash -c 'set -m ; setsid cat ; echo HELLO'

set -m вещь заставляет bash включить управление заданиями, что, по-видимому,необходим для предотвращения перенаправления командной строки в /dev/null.

Здесь echo HELLO представляет вашу "программу A".Команды seqsleep в середине) предназначены только для ввода.И да, вы можете направить все это на обработку B.

Примерно такое уродливое и непереносимое решение, которое вы могли бы попросить ...

1 голос
/ 08 сентября 2011

Чтобы сделать данный тестовый пример работающим, вы должны while ... read от управляющего оконечного устройства /dev/tty внутри конструкции sh -c '...'.

Обратите внимание на использование eval (можно ли здесь избежать?) И что многострочные команды на input> не будут работать.

echo 'ls; export var=myval'  | ( 
stdin="$(</dev/stdin)"
/bin/sh -i -c '
  eval "$1"; 
  while IFS="" read -e -r -p "input> " line; do 
    history -s "${line}"
    eval "${line}"; 
  done </dev/tty
' argv0 "${stdin}"
)

input> echo $var

Подобную проблему и использование именованных каналов смотрите здесь:

BASH: лучшая архитектура для чтения из двух входных потоков

1 голос
/ 23 июня 2011

Труба имеет два конца. Один предназначен для записи, а то, что пишется, появляется на другом конце, для чтения.

Это труба, а не переход T или Y.

Я не думаю, что ваш сценарий возможен. Наличие «ввода на ввод» ничего не имеет смысла.

0 голосов
/ 23 июня 2011

Похоже, это делает то, что вы хотите:

$ ( ./a <&-; cat ) | ./b

(Мне не ясно, хотите ли вы, чтобы a получал входные данные ... это решение отправляет все входные данные на b) Конечно, в этом случаевходы в b строго упорядочены: все выходные данные a сначала отправляются в b, затем завершается, затем ввод переходит в b.Если вы хотите чередовать вещи, попробуйте:

$ ( ./a <&- & cat ) | ./b
0 голосов
/ 23 июня 2011

Если я правильно понимаю ваши требования, вы хотите, чтобы это было настроено (искусство ASCII на первый план):

o----+----->|  A   |----+---->|  B  |---->o
     |                  ^
     |                  |
     +------------------+

с дополнительным ограничением на то, что, если процесс A закрывает магазин, процесс B должен иметь возможностьпереходите к потоку ввода, идущему к B.

Это нестандартная установка, как вы понимаете, и может быть достигнута только с помощью вспомогательной программы для ввода входа в A и B. В итоге вы получитенекоторые интересные проблемы с синхронизацией, но все они будут работать замечательно хорошо, если ваши сообщения достаточно короткие.

Необходимо отметить, что для этого необходимо установить два канала - один для ввода в A идругой для входа B, а выход A будет также подключен к входу B.

o---->|  C  |---------->|  A   |----+---->|  B  |---->o
         |                          ^
         |                          |
         +--------------------------+

Обратите внимание, что C будет записывать данные дважды, один раз в A и один раз в B.Также обратите внимание, что труба от A до B - это та же труба, что и труба от C до A.

0 голосов
/ 23 июня 2011

Это не может быть сделано точно так, как показано, но для выполнения вашего примера вы можете использовать способность cat объединять файлы:

cat <(echo ls) - | /bin/sh

(Вы можете сделать -i, но тогда вам придется заставить другой процесс уничтожить / bin / sh, так как ваши попытки Ctrl-C и Ctrl-D выходят из строя.)

Предполагается, что вы хотите передать свой ввод по каналу, а затем принять от stdin. Вы также можете сделать так, чтобы он что-то делал после выполнения стандартного ввода или с обеих сторон, но он не будет объединять ввод за символом или строку за строкой.

...