我正在执行一个带有标准输入重定向的程序:
$ prog < f
在这种情况下,标准输入将被完全缓冲。
有没有办法让它成为无缓冲或行缓冲?
编辑。
无需修改程序源码(即使用setvbuf())
答案1
不缓冲通常对于输出更有意义。输出缓冲是指应用程序在写入之前保留其输出,直到其累积到足够的量,从而最大限度地减少 I/O 数量。
在输入时,应用程序所能做的就是调整一次从输入中读取的字节数(好吧,至少是它请求的字节数,因为它不能保证收到尽可能多的字节数;文件当时可能有更少的可用字节,例如对于管道或 tty 设备)。
使用stdio
,取消缓冲输入流,将该缓冲区的大小设置为一个字节。
一次读取一个字节的效率很低,而且通常不需要这样做。
可能需要它的情况是从不可查找的输入(如管道,因此f
如果它是常规文件则不是您的)读取并且prog
需要在文件中的给定点停止读取,以便另一个进程可以恢复读取在那时候。
例如,在:
seq 10 | { grep -q 5; cat; }
如果你想cat
输出第 6 到 10 行,那就是在 wheregrep
停止读取文件之后的行(这里是一个管道,所以不可查找)。
上面的命令没有返回任何内容,因为已经一口气grep
读取了所有的输出。seq
请注意,如果您写的是:
{ seq 5; sleep 1; seq 6 10; } | { grep -q 5; cat; }
那会起作用的。grep
也请求一个大的缓冲区,但由于当时只有前 5 行可用,所以grep
处理它们并在第 5 行退出。换句话说,它不会累积其输入,直到缓冲区已满(或达到 eof)以开始处理它(我知道执行类似操作的唯一命令是mawk
)。
通过某些命令,在 GNU 和 FreeBSD 系统上,您可以使用 调整输入缓冲stdbuf -i
。使用stdbuf -i0
(取消缓冲)与(读入大小为 1 的缓冲区)相同stdbuf -i1
,并且会导致输入一次读取一个字节。
它不适用于 GNU grep
,但可以与 GNU 一起使用sed
:
$ seq 10 | { sed -n /5/q; cat; }
$ seq 10 | { stdbuf -i0 sed -n /5/q; cat; }
6
7
8
9
10
使用strace
,您可以看到 s 的大小read()
正在调整:
$ seq 5 | { strace -e read sed -n /2/q; cat; }
[...]
read(0, "1\n2\n3\n4\n5\n", 4096) = 10
+++ exited with 0 +++
$ seq 5 | { strace -e read stdbuf -i0 sed -n /2/q; cat; }
[...]
read(0, "1", 1) = 1
read(0, "\n", 1) = 1
read(0, "2", 1) = 1
read(0, "\n", 1) = 1
+++ exited with 0 +++
3
4
5
$ seq 5 | { strace -e read stdbuf -i1 sed -n /2/q; cat; }
[...]
read(0, "1", 1) = 1
read(0, "\n", 1) = 1
read(0, "2", 1) = 1
read(0, "\n", 1) = 1
+++ exited with 0 +++
3
4
5
答案2
不,那里没有。
您对问题的修改明确排除了这样做的方法。人们可以编辑程序来完成我们想要程序做的事情,或者使用一种工具来挂钩动态加载器和 C 运行时库的内部,以安排setvbuf
在程序启动时进行调用。
如果setvbuf
不允许使用该功能,则根本没有办法做到这一点。打电话setvbuf
是一个人需要做的事情。