![在不使用管道的情况下将标准输出重定向到终端和文件?](https://linux22.com/image/35091/%E5%9C%A8%E4%B8%8D%E4%BD%BF%E7%94%A8%E7%AE%A1%E9%81%93%E7%9A%84%E6%83%85%E5%86%B5%E4%B8%8B%E5%B0%86%E6%A0%87%E5%87%86%E8%BE%93%E5%87%BA%E9%87%8D%E5%AE%9A%E5%90%91%E5%88%B0%E7%BB%88%E7%AB%AF%E5%92%8C%E6%96%87%E4%BB%B6%EF%BC%9F.png)
我有代码,大概是这样的:
#!/bin/bash
VAR=0
func() {
VAR=$((VAR+1))
echo 'Logging information.'
}
func 2>&1 | tee 'log.txt'
echo "Should be 1: ${VAR}"
当调用它时会发生这种情况:
:~$ ./script.sh
Should be 1: 0
据我了解,这是因为我正在使用的管道正在生成一个子外壳。对 in 的更改VAR
不会向上传播,因此不会反映在输出中。
func
就我而言,这是一个相当漫长的过程,并且输出需要实时进行。因此,仅写入文件然后cat
读取该文件并不是一种选择。另外,我想避免将任何内容写入文件,然后如果可能的话将其作为变量读回。
那么有什么方法可以满足对管道的需要,或者我还不知道的 bash 技巧可以帮助我吗?
编辑:
尝试使用命名管道:
#!/bin/bash
VAR=0
func() {
VAR=$((VAR+1))
echo 'Logging information.'
sleep 5
}
mkfifo my_fifo
func >my_fifo 2>&1 &
tee 'log .txt' <my_fifo
echo "Should be 1: ${VAR}"
不幸的是结果是一样的:
:~$ ./script.sh
Should be 1: 0
答案1
你可以这样做:
func > >(tee log.txt) 2>&1
wait
您可以将文件描述符专用于日志记录:
exec 3> >(tee log.txt)
tee_pid=$!
func >&3 2>&1
...
但请注意,由于它tee
在后台运行,如果不是所有输出都经过它,那么输出的顺序可能会受到影响。
答案2
您可以使用 tmp 文件
func >tmpfile 2>&1
tee 'log.txt' <tmpfile
或先进先出
mkfifo pipe_replacement
tee 'log.txt' <pipe_replacement &
func >pipe_replacement 2>&1
答案3
我会这么做……
#!/bin/sh -x
run() if ! ps -p "$run" >&2
then n=0 run=$$ exec "$0" "$@" 2>&1 | {
! tee outfile ; }
fi 2>/dev/null
run "$@" || exit
fn() { var=val$n; echo "$((n+=1)): $var"; }
fn
sleep 5
fn
IN
首先检查它是否已经在另一个进程中打开了一个到 a 的管道tee
,如果没有,它exec
自己就进入一个进程中。之后,所有输出都会通过管道输出到脚本的其余部分,并且维护从那时到脚本退出之间设置的所有变量 - 事实上,这就是它首先进行检查的方式。
因此,运行上面的打印:
+ run
+ run
+ fn
+ var=val0
+ echo 1: val0
1: val0
+ sleep 5
+ fn
+ var=val1
+ echo 2: val1
2: val1
+ exit
然后运行cat outfile
打印...
+ run
+ run
+ fn
+ var=val0
+ echo 1: val0
1: val0
+ sleep 5
+ fn
+ var=val1
+ echo 2: val1
2: val1
+ exit
您也可以考虑使用sed
而不是tee
.虽然tee
性能会更高全输出在这种情况下,sed
可以一次写入多个文件,并且可以有条件地执行此操作。
例如,您可能会回显如下行:
echo 'LOG-ONLY: some message here'
聆听sed
过程可以做到:
sed -n '/^LOG-ONLY:/!p;s///w ./my_log.txt'
...这会w
在剥离部件后将该行写入文件LOG_ONLY
并避免将其打印到终端。