如何将 stdout 保存到一个文件,将 stderr 保存到另一个文件,将 stdout+stderr 保存到第三个文件,并像 shell 脚本一样将 stdout + stderr 保存到终端?
我在其他地方找到了这个:
exec > >(tee std_out) 2> >(tee err_out >&2)
ls # Should got to std_out
fsdfs # Command not found goes to err_out
这真的很接近。如果我运行,bash test.sh 2>&1 | tee output
那么它可以工作,但我无权访问我的脚本的运行方式。这是一个cid系统。我需要能够使用 exec 从脚本内部执行“组合输出”。
我正在创建一个 CI/CD 库,但我无法知道客户将使用该库做什么,因此我想考虑每个用例。
答案1
简单地扩展你的方法:
exec 2> >(tee -a stderr stdall) 1> >(tee -a stdout stdall)
标准错误将被写入名为 的文件stderr
,标准输出为stdout
,标准错误和标准输出也将被写入控制台(或exec
运行时两个文件描述符指向的任何内容)和stdall
。
tee -a
(append) 是必需的,以防止被开始写入的stdall
第二个数据覆盖。tee
注意命令其中执行重定向是相关的:第二个进程替换受到第一个重定向的影响,即它发出的错误将被发送到>(tee -a stderr stdall)
.当然,您可以将第二个进程替换的标准错误重定向到 以/dev/null
避免这种副作用。在标准错误之前重定向标准输出也会将每个错误发送到stdout
和stdall
。
由于 Bash 进程替换中的命令被执行异步地,无法保证它们的输出将按照生成的顺序显示。更糟糕的是,标准输出和标准错误的片段最终可能出现在同一行。
答案2
$0
您的脚本可以通过(通过设置和检查环境变量以避免无限递归)自行运行,而不是依赖 bash 的> >(...)
构造,而 IMLE 的构造是反复无常且不可靠的。
if [ "$REDIRECTED" != 1 ]; then
export REDIRECTED=1
set -o pipefail
{ { "$0" | tee stdout >&3; } 2>&1 | tee stderr; } 3>&1 | tee stdboth
exit
fi
# rest of your script here
由于tee
不使用行缓冲(也不能使用 强制这样做stdbuf(1)
),因此最终输出中将不考虑写入 stdout 和 stderr 的数据顺序。对于使用完全缓冲并同时写入 stdout 和 stderr 的命令,即使行缓冲也tee
无济于事,更糟糕的是,您可能会得到一半 stdout 和一半 stderr 的输出行。
我认为仅使用 shell 语言和现成的命令行实用程序无法解决此问题。
答案3
我正在创建一个 CI/CD 库,但我无法知道客户将使用该库做什么,因此我想考虑每个用例。
鉴于这种情况,我质疑 bash 是否需要处理输出。理想情况下,在这种情况下,您需要对输出添加时间戳并为其提供标准输出类型的 id,并且应用程序应该决定如何处理消息。