Как избежать эхо закрытия FIFO именованных каналов?- Забавное поведение Unix FIFO - PullRequest
49 голосов
/ 07 декабря 2011

Я хочу вывести некоторые данные в канал и заставить другой процесс что-то делать с данными построчно. Вот игрушечный пример:

mkfifo pipe
cat pipe&
cat >pipe

Теперь я могу вводить все, что захочу, и после нажатия Enter я сразу вижу ту же строку. Но если заменить вторую трубу на echo:

mkfifo pipe
cat pipe&
echo "some data" >pipe

Канал закрывается после echo и cat pipe&, так что я не могу передать больше данных через канал. Есть ли способ избежать закрытия канала и процесса, который получает данные, чтобы я мог пропустить много строк данных через канал из скрипта bash и обработать их по мере их поступления?

Ответы [ 6 ]

52 голосов
/ 08 декабря 2011

Поместите все операторы, которые вы хотите вывести в fifo, в один и тот же подоболочек:

# Create pipe and start reader.
mkfifo pipe
cat pipe &
# Write to pipe.
(
  echo one
  echo two
) >pipe

Если у вас есть сложности, вы можете открыть канал для записи:

# Create pipe and start reader.
mkfifo pipe
cat pipe &
# Open pipe for writing.
exec 3>pipe
echo one >&3
echo two >&3
# Close pipe.
exec 3>&-
47 голосов
/ 07 декабря 2011

Когда FIFO открыт для чтения, он блокирует вызывающий процесс (обычно).Когда процесс открывает FIFO для записи, считыватель разблокируется.Когда устройство записи закрывает FIFO, процесс чтения получает EOF (0 байтов для чтения), и больше ничего нельзя сделать, кроме как закрыть FIFO и открыть снова.Таким образом, вам нужно использовать цикл:

mkfifo pipe
(while cat pipe; do : Nothing; done &)
echo "some data" > pipe
echo "more data" > pipe

Альтернативой является сохранение какого-либо процесса с открытым FIFO.

mkfifo pipe
sleep 10000 > pipe &
cat pipe &
echo "some data" > pipe
echo "more data" > pipe
21 голосов
/ 03 ноября 2016

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

Итак, измените ваш второй пример на:

mkfifo pipe
cat <>pipe &
echo "some data" >pipe
2 голосов
/ 20 августа 2018

Честно говоря, лучший способ, которым я смог заставить это работать, был с помощью socat, который в основном соединяет два сокета.

mkfifo foo
socat $PWD/foo /dev/tty

Теперь в новом семестре вы можете:

echo "I am in your term!" > foo
# also (surprisingly) this works
clear > foo

Недостатком является то, что вам нужен socat, который не является основным инструментом, который получают все. Плюсом является то, что я не могу найти что-то, что не работает ... Я могу печатать цвета, tee на fifo, очищать экран и т. Д. Это как если бы вы работали с терминалом в целом.

2 голосов
/ 19 января 2015

Я улучшил вторую версию из ответа Джонатана Леффлера, чтобы поддержать закрытие канала:

dir=`mktemp -d /tmp/temp.XXX`
keep_pipe_open=$dir/keep_pipe_open
pipe=$dir/pipe

mkfifo $pipe
touch $keep_pipe_open

# Read from pipe:
cat < $pipe &

# Keep the pipe open:
while [ -f $keep_pipe_open ]; do sleep 1; done > $pipe &

# Write to pipe:
for i in {1..10}; do
  echo $i > $pipe
done

# close the pipe:
rm $keep_pipe_open
wait

rm -rf $dir
1 голос
/ 14 сентября 2014

В качестве альтернативы другим решениям здесь, вы можете вызвать cat в цикле как ввод вашей команды:

mkfifo pipe
(while true ; do cat pipe ; done) | bash

Теперь вы можете подавать команды по одному, и он не закроется:

echo "'echo hi'" > pipe
echo "'echo bye'" > pipe

Вам, конечно, придется убить процесс, если вы хотите, чтобы он прекратился. Я думаю, что это наиболее удобное решение, так как оно позволяет вам указывать несуществующее поведение при создании процесса.

...