bash 如何避免回声关闭 FIFO 命名管道?- Unix FIFO 的有趣行为

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8410439/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 21:18:19  来源:igfitidea点击:

How to avoid echo closing FIFO named pipes? - Funny behavior of Unix FIFOs

bashunixpipeecho

提问by user1084871

I want to output some data to a pipe and have the other process do something to the data line by line. Here is a toy example:

我想将一些数据输出到管道,并让其他进程逐行对数据执行某些操作。这是一个玩具示例:

mkfifo pipe
cat pipe&
cat >pipe

Now I can enter whatever I want, and after pressing enter I immediately see the same line. But if substitute second pipe with echo:

现在我可以输入任何我想要的,按下回车后我立即看到同一行。但是,如果用以下内容替换第二个管道echo

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

The pipe closes after echoand cat pipe&finishes so that I cannot pass any more data through the pipe. Is there a way to avoid closing the pipe and the process that receives the data, so that I can pass many lines of data through the pipe from a bash script and have them processed as they arrive?

管道在完成后关闭echocat pipe&因此我无法通过管道传递更多数据。有没有办法避免关闭管道和接收数据的进程,以便我可以从 bash 脚本通过管道传递多行数据,并在它们到达时进行处理?

采纳答案by Jonathan Leffler

When a FIFO is opened for reading, it blocks the calling process (normally). When a process opens the FIFO for writing, then the reader is unblocked. When the writer closes the FIFO, the reading process gets EOF (0 bytes to read), and there is nothing further that can be done except close the FIFO and reopen. Thus, you need to use a loop:

当 FIFO 打开读取时,它会阻塞调用进程(通常)。当进程打开 FIFO 进行写入时,读取器将被解除阻塞。当写者关闭 FIFO 时,读取进程得到 EOF(要读取的 0 个字节),除了关闭 FIFO 并重新打开之外,没有其他可以做的事情。因此,您需要使用循环:

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

An alternative is to keep some process with the FIFO open.

另一种方法是在 FIFO 打开的情况下保持某些进程。

mkfifo pipe
sleep 10000 > pipe &
cat pipe &
echo "some data" > pipe
echo "more data" > pipe

回答by Mark Edgar

Put all the statements you want to output to the fifo in the same subshell:

将所有要输出到 fifo 的语句放在同一个子 shell 中:

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

If you have some more complexity, you can open the pipe for writing:

如果你有一些更复杂的,你可以打开管道进行写入:

# 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>&-

回答by Patrick

You can solve this very easily by opening the read side of the pipe in read-write mode. The reader only gets an EOF once the last writer closes. So opening it in read-write makes sure there is always at least one writer.

您可以通过以读写模式打开管道的读取端来轻松解决此问题。一旦最后一个写入器关闭,读取器只会得到一个 EOF。所以以读写方式打开它可以确保至少有一个写入器。

So change your second example to:

因此,将您的第二个示例更改为:

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

回答by Jordan

Honestly, the best way I was able to get this to work was by using socat, which basically connections two sockets.

老实说,我能够让它工作的最好方法是使用socat,它基本上连接了两个套接字。

mkfifo foo
socat $PWD/foo /dev/tty

Now in a new term, you can:

现在,在一个新术语中,您可以:

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

The downside is you need socat, which isn't a basic util everyone gets. The plus side is, I can't find something that doesn't work... I am able to print colors, teeto the fifo, clear the screen, etc. It is as if you slave the whole terminal.

缺点是您需要 socat,这不是每个人都能获得的基本实用程序。好的一面是,我找不到不起作用的东西......我能够打印颜色,tee先进先出,清除屏幕等。就好像你奴隶整个终端。

回答by silyevsk

I enhanced the second version from the Jonathan Leffler's answer to support closing the pipe:

我从 Jonathan Leffler 的回答中增强了第二个版本以支持关闭管道:

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

回答by Adam Dingle

As an alternative to the other solutions here, you can call catin a loop as the input to your command:

作为此处其他解决方案的替代方案,您可以cat在循环中调用作为命令的输入:

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

Now you can feed it commands one at a time and it won't close:

现在你可以一次给它一个命令,它不会关闭:

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

You'll have to kill the process when you want it gone, of course. I think this is the most convenient solution since it lets you specify the non-exiting behavior as you create the process.

当然,当您想要它消失时,您必须终止该进程。我认为这是最方便的解决方案,因为它允许您在创建流程时指定非退出行为。