我正在从另一个生成输出的进程(慢和无限)读取输出。 因为我想实时读取这些数据,我使用“stdbuf -oL”(行缓冲,数据是文本)。 我没有控制生成过程,所以我不能修改源来强制刷新。
到目前为止,stdbuf工作得很好,但是该进程使用SOCK_RAW,并且需要以root身份运行,具有setuid(0)或cap_net_raw
function。 以非root用户身份运行setuid或functionstdbuf似乎被忽略。 让我来certificate这个问题:
这是一个简单的作家:
#include <stdio.h> #include <unistd.h> int main(){ int i; for ( i = 0;; i++){ fprintf(stdout, "%d\n", i); sleep(1); } }
还有一个简单的读者:
#include <stdio.h> int main(){ char* line = NULL; size_t n = 0; while (getline(&line, &n, stdin) != -1 ) { fputs(line, stdout); } }
正如所料,通过执行./writer | ./reader
./writer | ./reader
什么也没有显示,直到填充缓冲区。 预先写入stdbuf -oL
启用行缓冲,并将行读入读取器:
% stdbuf -oL ./writer | ./reader 0 1 2 ...
但是,如果我添加cap_net_raw+ep
停止工作:
% sudo setcap cap_net_raw+ep ./writer % stdbuf -oL ./writer | ./reader (no output)
使用setuid时会观察到相同的行为:
% sudo chown root:root ./writer % sudo chmod +s ./writer % stdbuf -oL ./writer | ./reader (no output)
我有兴趣了解为什么发生这种情况,以及如何继续使用stdbuf而不以root身份运行。 我承认我并不完全理解幕后的setuid在做什么。
从stdbuf源代码看,它看起来像通过设置LD_PRELOAD工作。 使用LD_PRELOAD和setuid可执行文件或sudo当然有安全问题。
我发现的一个建议是禁用可执行文件的noatsecure selinux属性 。
另一个更简单的选项是避免stdbuf,直接从源代码中直接调用fflush(stdout)
。
LD_PRELOAD
解决方案 您可以使用expect
( expect-devel
)包中的一部分的unbuffer
实用程序。 unbuffer
是一个很短的期望脚本。 它不需要LD_PRELOAD
因为它使用了另一个技巧。 expect
创建一个伪终端(如xterm
或ssh
),因此使用unbuffer
执行的进程被愚弄,认为它正在写入交互式设备,因此默认情况下它会在stdout
上使用行缓冲。
在你的情况下的用法:
unbuffer ./writer | ./reader
如果stdbuf
与程序一起工作, unbuffer
也会以很高的概率工作。 由于LD_PRELOAD
带来一些限制, unbuffer
比stdbuf
有优势。 与stdbuf
相反,它将与这些类型的可执行文件一起工作:
libc