我需要将 BASH 中命令组的输出捕获到 STDOUT 和日志文件。考虑此代码与命令分组及其输出
#!/usr/bin/bash
main(){
declare -i mycode=1
echo "Declared mycode:${mycode}"
{
#command group
echo "mycode:${mycode}"
mycode=2
echo "mycode:${mycode}"
} 2>&1
echo "mycode:${mycode}"
}
main
输出是:
Declared mycode:1
mycode:1
mycode:2
mycode:2
我需要将命令组输出捕获到日志文件和 STDOUT,因此我添加 tee,如下所示:
#!/usr/bin/bash
main(){
declare -i mycode=1
echo "Declared mycode:${mycode}"
{
#command group
echo "mycode:${mycode}"
mycode=2
echo "mycode:${mycode}"
} 2>&1 | tee ~/log.log
echo "mycode:${mycode}"
}
main
但现在输出如下:
Declared mycode:1
mycode:1
mycode:2
mycode:1
所以价值我的代码当使用 tee 时,变量不会在外部作用域中设置为 2,因为 tee 的左侧将在子 shell 中运行。由于各种原因我需要我的代码在全局范围内定义,因此我需要避免使用子 shell。
如何在没有子 shell 的情况下实现 tee 的行为,从而将输出流式传输到 STDOUT 和日志文件。
答案1
解决这个问题的一种方法是自己实现管道连接器:
#!/bin/bash
# Initialisation
mycode=1
# Tidy up
trap 'ss=$?; [ -n "$tmpd" ] && [ -d "$tmpd" ] && rm -rf "$tmpd"; exit $ss' 1 2 15
# Unique temporary directory
tmpd=$(mktemp --directory "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX")
# Create pipe
pipe="$tmpd/pipe"
mknod "$pipe" p
# Output
tee <"$pipe" "$HOME/log.log" &
# Subprocess
{
echo "mycode:${mycode}"
mycode=2
echo "mycode:${mycode}"
} >"$pipe" 2>&1
# Destroy temporary, including pipe
wait
rm -rf "$tmpd"
# Done
echo "final mycode:${mycode}"
输出
mycode:1
mycode:2
final mycode:2
还cat ~/log.log
mycode:1
mycode:2
答案2
根据您想要通过日志文件实现的目标,您可能希望将其附加到描述符 #3。 (记住标准输出是#1并且标准错误是#3。)
#!/bin/bash
# Initialisation
mycode=1
# Attach fd3 to the logfile and stdout
exec 3> >(tee "$HOME/log.log" >&1)
# Demonstrations
echo this is to stdout
echo this is to stderr >&2
echo this is to logfile and stdout >&3
# Subprocess
{
echo "mycode:${mycode}"
mycode=2
echo "mycode:${mycode}"
} 2>&1 1>&3
# Done
echo "final mycode:${mycode}"
sleep 0.25
这种方法的一个优点是,只需重定向到文件描述符 3,就可以很容易地从任何地方写入日志文件(请参阅上面的“演示”代码块)。
缺点是tee
异步运行,因此输出可能会错误地交错。最后sleep 0.25
的目的是留出时间来tee
完成其输出的编写。根据您使用该功能的方式,交错可能是也可能不是现实世界的问题。
输出示例:
this is to stdout
this is to stderr
final mycode:2
this is to logfile and stdout
mycode:1
mycode:2
日志档案 (cat "$HOME/log.log"
):
this is to logfile and stdout
mycode:1
mycode:2