将 *unbuffered* 标准输出从 bash 脚本本身复制到文件

Posted

技术标签:

【中文标题】将 *unbuffered* 标准输出从 bash 脚本本身复制到文件【英文标题】:Copy *unbuffered* stdout to file from within bash script itself 【发布时间】:2015-06-22 08:34:26 【问题描述】:

我想将标准输出从 within 复制到一个 bash 脚本的日志文件中,这意味着我不想调用通过管道输出到 tee 的脚本,我希望脚本本身来处理它。我已经成功使用this 回答来完成此操作,使用以下代码:

#!/bin/bash
exec > >(sed "s/^/[$1] /" | tee -a myscript.log)
exec 2>&1

# <rest of script>
echo "hello"
sleep 10
echo "world"

这可行,但在脚本完成之前输出被缓冲的缺点,正如链接答案中所讨论的那样。在上面的例子中,“hello”和“world”都会在 10 秒后才显示在日志中。

我知道 stdbuf 命令,如果使用

运行脚本
stdbuf -oL ./myscript.sh 

然后 stdout 确实连续打印到文件和终端。 但是,我也希望在脚本中处理这个问题。有没有办法将这两种解决方案结合起来?我宁愿不求助于包装脚本,它只是调用包含“stdbuf -oL”的原始脚本。

【问题讨论】:

【参考方案1】:

如果存在特殊参数,您可以使用解决方法并使用 stdbuf 使脚本自行执行:

#!/bin/bash

if [[ "$1" != __BUFFERED__ ]]; then
    prog="$0"
    stdbuf -oL "$prog" __BUFFERED__ "$@"
else
    shift #discard __BUFFERED__

    exec > >(sed "s/^/[$1] /" | tee -a myscript.log)
    exec 2>&1

    # <rest of script>
    echo "hello"
    sleep 1
    echo "world"
fi

这将主要工作:

如果您使用./test 运行脚本,它会显示未缓冲的[] hello\n[] world。 如果您使用./test 123 456 运行脚本,它会显示您想要的[123] hello\n[123] world。 但是,如果您使用bash test 运行它,它将不起作用 - $0 设置为test,这不是您的脚本。解决这个问题不在这个问题的范围内。

【讨论】:

谢谢。这些限制确实没问题。 Tbh 这可能不是最干净的解决方案 :-) 但我会试试的。【参考方案2】:

第一个解决方案的延迟是由sed 引起的,而不是由tee 引起的。试试这个:

#!/bin/bash
exec 6>&1 2>&1>&>(tee -a myscript.log)

要“撤消”tee 效果:

exec 1>&6 2>&6 6>&-

【讨论】:

谢谢。你确定 tee vs sed 吗?很确定在没有 sed 的情况下也会发生同样的事情,但我会仔细检查。另外,您介意解释一下您的建议是做什么的吗? 6 是什么? 我遇到了similar 问题(乍一看并没有完全连接到您的问题),其中我试图通过将某个进程的标准输出传送到 sed/perl 来dos2unix 某个进程的输出/tr/dos2unix/etc 并同时使用tee 复制它。当两者都启用时,我无法摆脱烦人的延迟/缓冲效果。我最终决定tee only 并在以后执行dos2unix。有关将 stdout/stderr 重定向到 fd6 here 的更多信息 sed 上的 -u, --unbuffered 选项应该,正如 help 所说,load minimal amounts of data from the input files and flush the output buffers more often

以上是关于将 *unbuffered* 标准输出从 bash 脚本本身复制到文件的主要内容,如果未能解决你的问题,请参考以下文章

Linux python -u参数,设置无缓存输出(unbuffered)(无缓冲不缓冲不缓存)(实时重定向输出)

将所有输出重定向到 Bash 中的文件 [重复]

从 bash 脚本本身将 stdout 的副本重定向到日志文件

unbuffered I/O. buffered I/O<转载;

Linux Bash - 修改从标准输出中提取的文本

终端使用bash的标准输入输出