管道生产者如何告诉管道消费者它已到达“文件结尾”?”(未命名管道,而不是命名管道)

Cra*_*cks 3 pipe here-document here-string

我有一个应用程序,它要求生产者将文件名发送给消费者,并让生产者在发送最后一个文件名并到达文件末尾时向消费者指示。

为简单起见,在以下示例中, 使用和演示生产者,而使用 演示消费者。我试图推断“此处文件”方法但没有成功,用于向生产者包装器(如果存在此类东西)指示要查找的内容作为文件结尾的指示。如果它工作应该从输出中过滤。echoprintfcat<<EOFcatEOF

例 1)

输入

{
echo "Hello World!" 
printf '\x04' 
echo "EOF"
} <<EOF |\
cat
Run Code Online (Sandbox Code Playgroud)

输出

bash: warning: here-document at line 146 delimited by end-of-file (wanted `EOF')
Hello World!
EOF
Run Code Online (Sandbox Code Playgroud)

例 2)

输入

{ 
echo "Hello World!" 
printf '\x04' 
echo "EOF"
} |\
cat <<EOF
Run Code Online (Sandbox Code Playgroud)

输出

bash: warning: here-document at line 153 delimited by end-of-file (wanted `EOF')
Run Code Online (Sandbox Code Playgroud)

用于指示分隔符的“此处文件”方法仅适用于静态文本,而不适用于动态创建的文本,是否正确?

——实际应用——

inotifywait -m --format '%w%f' /Dir |  <consumer>
Run Code Online (Sandbox Code Playgroud)

消费者正在等待将文件写入目录 /Dir。如果在写入文件“/Dir/EOF”时,消费者只需通过编写如下 shell 脚本来检测逻辑文件结束条件,那就太好了:

inotifywait -m --format '%w%f' /Dir |<</Dir/EOF  <consumer>
Run Code Online (Sandbox Code Playgroud)

——回应贾尔斯的回答——

理论上可以实现吗

cat <<EOF
hello
world
EOF
Run Code Online (Sandbox Code Playgroud)

作为

SpecialSymbol="EOF"
{
    echo hello
    echo world
    echo $SpecialSymbol
} |\
while read Line; do 
  if [[ $Line == $SpecialSymbol ]]
    break
  else 
    echo $Line
  fi
done |\
cat
Run Code Online (Sandbox Code Playgroud)

理论上可能我的意思是“它是否支持现有的使用模式并且只启用以前是非法语法的额外使用模式?” - 意味着不会破坏现有的法律代码。

Sté*_*las 6

对于管道,一旦所有生产者关闭了管道的文件描述符并且消费者读取了所有数据,消费者就会看到文件的结尾。

所以,在:

{
  echo foo
  echo bar
} | cat
Run Code Online (Sandbox Code Playgroud)

cat一旦第二个echo终止并cat读取了foo\n和 ,就会看到文件结尾bar\n。你没有什么可做的了。

但要记住的是,如果管道左侧的某些命令启动了某个后台进程,则该后台进程将继承管道的 fd(其标准输出),因此cat在该进程也终止之前不会看到 eof或关闭其标准输出。如:

{
  echo foo
  sleep 10 &
  echo bar
} | cat
Run Code Online (Sandbox Code Playgroud)

你看到cat在 10 秒过去之前没有返回。

在这里,您可能希望将sleep的 stdout重定向到其他内容,例如,/dev/null如果您不希望将其(非)输出提供给cat

{
  echo foo
  sleep 10 > /dev/null &
  echo bar
} | cat
Run Code Online (Sandbox Code Playgroud)

如果您希望在|运行左侧子shell中的最后一个命令之前关闭管道的写入端,您可以关闭stdout或重定向到子shell中间的子shell exec,例如:

{
  echo foo
  exec > /dev/null
  sleep 10
} | (cat; echo "cat is now gone")
Run Code Online (Sandbox Code Playgroud)

但是请注意,除了cat命令之外,大多数外壳程序仍将等待该子外壳程序。因此,虽然您会立即看到cat is now gonefoo阅读后),但您仍然需要等待 10 秒钟才能完成整个管道。当然,在上面的那个例子中,写它会更有意义:

echo foo | cat
sleep 10
Run Code Online (Sandbox Code Playgroud)

<<ANYTHING...content...ANYTHING是一个 here-document,它使 command 的 stdin 成为一个包含内容的文件。在那里用处不大。\4是字节,当从终端读取时,终端设备持有的数据将刷新到从中读取的应用程序(当没有数据时,read()返回 0,这意味着文件结束)。同样,这里没有任何用处。