在不使用管道的情况下将标准输出重定向到终端和文件?

在不使用管道的情况下将标准输出重定向到终端和文件?

我有代码,大概是这样的:

#!/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并避免将其打印到终端。

相关内容