Как избежать эхо-закрытия FIFO именованных каналов? - Забавное поведение Unix FIFO
Я хочу вывести некоторые данные в канал, а другой процесс сделать что-то по строкам данных. Вот пример игрушки:
mkfifo pipe
cat pipe&
cat >pipe
Теперь я могу ввести все, что захочу, и после нажатия enter я сразу вижу ту же строку. Но если заменить вторую трубку на echo
:
mkfifo pipe
cat pipe&
echo "some data" >pipe
Труба закрывается после завершения echo
и cat pipe&
, чтобы я не мог передавать больше данных через трубу. Есть ли способ избежать закрытия трубы и процесса, который получает данные, чтобы я мог передавать много строк данных через канал из bash script и обрабатывать их по мере их поступления?
Ответы
Ответ 1
Когда 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
Ответ 2
Поместите все операторы, которые вы хотите вывести на 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>&-
Ответ 3
Это можно легко решить, открыв канал чтения в режиме чтения-записи. Читатель получает EOF только после закрытия последнего автора. Поэтому открытие его в режиме чтения-записи гарантирует, что всегда есть хотя бы один писатель.
Итак, измените свой второй пример на:
mkfifo pipe
cat <>pipe &
echo "some data" >pipe
Ответ 4
В качестве альтернативы другим решениям здесь вы можете вызвать cat
в цикле в качестве входа в свою команду:
mkfifo pipe
(while true ; do cat pipe ; done) | bash
Теперь вы можете подавать команды по одному и не закрывать:
echo "'echo hi'" > pipe
echo "'echo bye'" > pipe
Вам, конечно, придется убить процесс, если хотите. Я думаю, что это самое удобное решение, поскольку оно позволяет вам указать не выходящее поведение при создании процесса.
Ответ 5
Я улучшил вторую версию ответа Джонатана Леффлера, чтобы поддержать закрытие трубы:
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