我正在尝试编写一个简单的 python 脚本,该脚本从 fifo 读取,然后写入另一个 fifo。
我使用以下命令创建了两个 FIFO:
$ mkfifo input
$ mkfifo output
我使用以下命令调用该脚本:
$ tail -f input | stdbuf -oL ../entropyCalc/entropy.py > output
,使用以下命令观察 FIFO output
:
$ tail -f output
然后使用以下命令调用编写器:
$ echo "/path/to/a/valid/file" >> input
问题是我希望 fifo 在处理输入文件后立即输出结果,但我只观察到当我调用(执行)脚本一次时,退出然后重新执行脚本。此后一切正常。
总之:如果执行脚本 -> 启动读取器 -> 写入fifo ,则
在读取器中看不到任何输出但是,当我执行脚本 -> 启动读取器 -> 写入 fifo 时, 该命令会输出结果-> 终止脚本 -> 重新执行脚本input
tail -f output
我不确定是什么导致了这种行为,因为我希望系统在结果写入标准输出后立即写入文件。如果我不使用stdbuf -oL
限制缓冲到一行的缓冲,我会期望缓冲。
python 脚本是一个简单的熵计算器:
#! /usr/bin/env python2
import sys, os
import numpy as np
from scipy.stats import entropy
while 1:
try:
line = sys.stdin.readline()
except KeyboardInterrupt:
break
if not line:
break
line = line.strip()
if not line == '':
fname1 = open(line)
fsize = os.path.getsize(line)
f1 = np.frombuffer(fname1.read(fsize), dtype=np.uint8)
value,counts = np.unique(f1, return_counts=True)
print line,str(entropy(counts))
sys.stdout.flush()
我在 Ubuntu 18.04.3 上使用 bash 4.4
答案1
你用tail
错了。
您所观察到的只是表明tail -f output
正如广告所宣传的那样。而且你的使用过程中还有更多的陷阱tail -f input
。
请注意,这些tail
误用也会破坏脚本的操作完整性。详情请参阅下文...
简短说明
您的使用问题tail -f input
:
- 它首先等待文件结尾。
- 一旦文件结束第一的到达(第一个文件名提供者终止),它只会将最后 10 个文件名传递给脚本;破坏运营诚信。
- 它缓冲直到达到每个文件结尾,使得交互式/稀疏文件名输入不可用。
您的使用问题tail -f output
:
- 它首先等待文件结尾。
- 一旦文件结束第一的达到(你的脚本第一次被杀死),到目前为止它只会给出脚本输出的最后 10 行;破坏运营诚信。
(缓冲不是问题,tail -f output
因为它的输出腿是终端)
看结论以下为补救措施。
长解释
的工作描述tail
基本上是:“输出之前的10行文件结尾”。-f
开关仅添加“以及随后附加的所有内容”。
- 为了实现其主要目标,
tail
必须不断地无声地、无休止地读取输入(缓冲区中仅保留最新的 10 行),直到到达文件结尾。然后它会将缓冲区中的最后 10 行转储到输出,然后退出(或继续同-f
相操作)。
现在,让我提醒你文件结尾于管道在上游程序终止之前永远不会出现1 ...
结果就是tail -f output
意志,按设计,保持默默地缓冲脚本的结果,而不发出任何内容......直到您第一次终止脚本的那一刻:文件结尾被发送到tail -f output
,那就是您开始看到输出的时候。
但你看到的并不是完整的输出到目前为止,而是仅输出最后 10 行在那时候。
如果您要求脚本分析 20 个文件(一次少于 10 个文件,请参阅下一点),您将仅获得在第一次终止脚本之前分析的最后 10 个文件的结果。这打破了脚本操作的期望(即完整性),其中的结果每一个所做的分析应该在输出中报告。
这个问题也影响
tail -f input
。将包含 11 个以上文件名的文件列表转储到input
管道中第一次,将导致只有 10 个最底部的名称到达您的脚本,破坏脚本操作的完整性再次。第二次及以后,这将不再是问题。
故事还没有结束……
一旦到达第一个文件结尾,tail -f
将继续分阶段操作-f
,这意味着输出随后附加到输入文件的所有内容。
在这个阶段,它还不会关闭输入句柄;它将注册一个inotify
输入监听器文件,然后等待。每次任何程序写入/关闭输入文件时,tail
都会再次读取输入句柄(并且输出刚刚读到的所有内容),直到遇到文件结尾2,然后又回到等待状态。冲洗并重复,直到手动终止tail -f
。
这里的一个问题是“输出刚刚读到的所有内容”也受到通常的输出缓冲当输出不是终端时。
它不会影响您
tail -f output
本身的使用(因为它的输出支路是一个终端)。tail -f input
但是,将会受到影响:- 如果您使用 交互地输入多个文件名
cat > input
,您将看到在您终止之前处理不会开始cat
。 - 这意味着将
input
管道连接到仅偶尔发出文件名的长时间运行的程序也不起作用。
- 如果您使用 交互地输入多个文件名
结论
根据我的理解,您曾经tail -f input
屏蔽脚本接收文件结尾,因此您的脚本可以像守护进程,而文件名提供者程序可以来来去去。
所以我的建议是使用tail -n +1 -f
它,以确保tail
不要无休止地寻找文件结尾,并消除最后 10 行问题。然后用stdbuf
它来控制可能成为问题的缓冲3,4 ...
stdbuf -oL tail -n +1 -f input | ../entropyCalc/entropy.py > output 2>&1
然后在监控端,使用:
tail -n +1 -f output
或者:
while true; do cat output; done
脚注
- 1或者更准确地说,当上游程序关闭管道时。 (在你的情况下,它只会发生
tail -f output
在你的脚本终止时) - 2永远不会发生在你的身上
tail -f output
,除非你终止了你的脚本第二时间。 - 3您的脚本已经在其输出阶段进行了行缓冲,因此不需要
stdbuf
那里。 - 4标准错误重定向
2>&1
还将确保脚本错误也显示在监控终端上。