Bash – Redirecting stdout to terminal and file without using a pipe

bashpipeshell-script

I have code, that goes something like this:

#!/bin/bash

VAR=0

func() {
    VAR=$((VAR+1))
    echo 'Logging information.'
}

func 2>&1 | tee 'log.txt'

echo "Should be 1: ${VAR}"

When calling it this happens:

:~$ ./script.sh
Should be 1: 0

As far as I understand it, this is because the pipe I'm using is spawning a subshell. The changes to VAR in there are not propagated up and therefore are not reflected in the output.

func in my case is a rather lengthy process and the output would need to be in real time. So just writing to a file and then cating the file is not an option. Also I would like to avoid writing anything to file and later reading it back in as a Variable if possible.

So is there any way to get the need for the pipe out of there, or a bash trick I don't know yet, that could help me?

EDIT:

Tried with a named pipe:

#!/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}"

Result is unfortunately the same:

:~$ ./script.sh
Should be 1: 0

Best Answer

You can do something like:

func > >(tee log.txt) 2>&1
wait

You can dedicate a file descriptor for logging:

exec 3> >(tee log.txt)
tee_pid=$!

func >&3 2>&1
...

Beware though that as that tee runs in background, if not all the output goes through it, then the order in the output may be affected.

Related Question