防止FIFO关闭/重新使用已关闭的FIFO

20
考虑以下场景:
创建了一个名为test的FIFO。在一个终端窗口(A)中运行cat < test,在另一个窗口(B)中运行cat > test。现在可以在窗口B中写入并在窗口A中获得输出。如果您终止进程A并重新启动它,则仍然可以像预期那样使用此设置。但是,如果您终止窗口B中的进程,则B将(就我所知)通过FIFO向进程A发送EOF并终止该进程。
实际上,如果您运行不会在EOF上终止的进程,则仍然无法使用重定向到该进程的FIFO。我认为这是因为此FIFO被视为关闭状态。
有没有办法解决这个问题?
我遇到这个问题的原因是我想将命令发送到在屏幕会话中运行的我的Minecraft服务器。例如:echo“command”> FIFO_to_server。可能可以仅使用屏幕本身来执行此操作,但我对屏幕不太熟悉,我认为仅使用管道的解决方案将是更简单,更清洁的方法。
4个回答

35

A正在从一个文件中读取内容。当它到达文件末尾时,它会停止读取。即使文件是一个FIFO,这也是正常行为。现在你有四种方法。

  1. 更改读取器的代码使其在文件末尾后继续读取。这意味着输入文件是无限的,到达文件末尾只是一种幻觉。对你来说不太实际,因为你需要更改Minecraft服务器的代码。
  2. 应用Unix哲学。您有一个不同意协议的写入器和读取器,因此您可以插入一个连接它们的工具。恰好,在Unix工具箱中有这样一个工具:tail -ftail -f在看到文件结尾后仍会持续从其输入文件中读取。让所有客户端都与管道通信,并将tail -f连接到Minecraft服务器:

    tail -n +1 -f client_pipe | minecraft_server &
    
  3. 正如jilles所提到的,可以使用一个技巧:管道支持多个写入端,只有在最后一个写入端关闭时才会关闭。因此,请确保始终存在一个不会关闭的客户端。

  4. while true; do sleep 999999999; done >client_pipe &
    
    问题在于该服务器基本上是设计为处理单个客户端的。如果要处理多个客户端,应该改用套接字(socket)进行通信。将套接字视为“元管道”:连接到套接字会创建一个管道,一旦客户端断开连接,该特定管道就关闭了,但服务器可以接受更多连接。这是一个干净的方法,因为它还确保如果两个客户端恰好同时连接(使用管道,它们的命令可能会交错),你也不会有混淆的数据。然而,这需要改变Minecraft服务器。

1
不幸的是,tail 等待 EOF,因此它不会逐行传递管道中的内容。 - pabouk - Ukraine stay strong
2
@pabouk 感谢您指出这个错误:tail -n 1 -f 会跳过在它开始或读取速度比它快的输入。我本意是写 tail -n +1 -f,它可以立即开始输出。 - Gilles 'SO- stop being evil'
2
谢谢。我之前尝试过-n +1,但它并没有按照我的预期工作。现在你确认这是正确的方法后,我进一步检查了问题,并意识到问题出在tail的标准输出块缓冲(而不是默认的行缓冲)上。逐行传输的解决方案:stdbuf -oL tail -n +1 -f client_pipe | command - pabouk - Ukraine stay strong
2
有一种更简单的方法来确保管道至少有一个写入者:让读取器以读写模式打开它:minecraft_server <>client_pipe& - phemmer

8

启动一个进程,使FIFO保持打开状态以进行写入,并无限期地运行。这将防止读取器看到文件结束条件。


1

来自 这个答案 -

在一些像Linux这样的系统上,使用<>可以打开命名管道(FIFO)而不会阻塞(不会等待其他进程打开另一端),并确保管道结构保持活动状态。例如:

所以你可以这样做:

cat <>up_stream >down_stream

# the `cat pipeline keeps running
echo 1 > up_stream  
echo 2 > up_stream
echo 3 > up_stream

然而,我找不到关于这种行为的文档。因此,这可能是某些系统特定的实现细节。我在 MacOS 上尝试过上述方法,它可以正常工作。


0

您可以通过在“mkfifo yourpipe”中使用带有分号的括号添加所需内容,将多个输入添加到管道中:

(cat file1; cat file2; ls -l;) > yourpipe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接