关于logging:bash:强制exec’d进程有无缓冲的stdout

bash: force exec'd process to have unbuffered stdout

我有一个剧本,像:

1
2
#!/bin/bash
exec /usr/bin/some_binary > /tmp/my.log 2>&1

问题是,some_binary将其所有日志发送到stdout,缓冲使之成为输出,所以我只看到几行中的一块输出。当有什么东西卡住了,我需要看看最后一行是怎么说的,这很烦人。

在我执行将影响某些二进制文件的exec之前,是否有任何方法可以使stdout不受缓冲,从而使其具有更有用的日志记录?

(包装脚本只在exec之前设置了几个环境变量,所以用perl或python解决方案也是可行的。)


GNU coreutils-8.5还具有修改I/O流缓冲的stdbuf命令:

http://www.pixelbeat.org/programming/stdio_缓冲/

因此,在您的示例中,只需调用:

1
stdbuf -oL /usr/bin/some_binary > /tmp/my.log 2>&1

这将允许文本立即逐行显示(一旦一行以C中的行"
"
字符结尾完成)。如果您真的想要立即输出,请使用-o0

如果您不想通过unbuffer命令引入对expect的依赖性,那么这种方法可能更可取。另一方面,如果你不得不欺骗some_binary认为它正面临着一个真正的tty标准输出,那么需要使用unbuffer方式。


您可能会发现expect附带的unbuffer脚本可能会有所帮助。


一些命令行程序可以选择修改它们的stdout流缓冲行为。如果C源可用的话,这就是方法…

1
2
3
4
5
6
7
8
9
10
11
12
13
# two command options ...
man file | less -p '--no-buffer'
man grep | less -p '--line-buffered'

# ... and their respective source code

# from: http://www.opensource.apple.com/source/file/file-6.2.1/file/src/file.c
if(nobuffer)
   (void) fflush(stdout);

# from: http://www.opensource.apple.com/source/grep/grep-28/grep/src/grep.c
if (line_buffered)
   fflush (stdout);

作为使用expect的unbuffer脚本或修改程序源代码的替代方法,您还可以尝试使用脚本(1)来避免管道引起的stdout中断:

请参见:欺骗应用程序使其认为其stdin是交互式的,而不是管道

1
2
3
4
5
# Linux
script -c"[executable string]" /dev/null

# FreeBSD, Mac OS X
script -q /dev/null"[executable string]"


我搜遍互联网寻找答案,但这些都不适用于uniq,因为stdbuf太顽固,除了stdbuf之外,它无法缓冲一切:

{piped_command_here} | stdbuf -oL uniq | {more_piped_command_here}


环境变量可以将终端IO模式设置为无缓冲。

export nsunbufferedio=是

这将为C和Ojitive-C终端输出命令设置终端无缓冲。


GNUCoreutils-8包含一个名为stdbuf的程序,它基本上完成了ld_预加载技巧。它在Linux上工作,据报道它在BSD系统上工作。