How to print shell script stdout/stderr to file/s and console

Viewed 1739

In the bash script I use the following syntax I order to print everything from the script to the files - $file and $sec_file

we are running the script on our Linux rhel server - version 7.8

exec > >(tee -a "$file" >>"$sec_file") 2>&1

so after bash script completed , we get on both files the content of stdout/stderr of every line in the bash script

now we want additionally to print to the console the stdout/stderr and not only to the files

I will appreciate of any suggestion

Example of the script:

# more  /tmp/script.bash

#!/bin/bash

file=/tmp/file.txt
sec_file=/tmp/sec_file.txt

exec > >(tee -a "$file" >>"$sec_file") 2>&1

echo "hello world , we are very happy to stay here "

Example how to run the script:

/tmp/script.bash

<--  no output from the script -->

# more /tmp/file.txt
hello world , we are very happy to stay here
# more /tmp/sec_file.txt
hello world , we are very happy to stay here

example of expected output that should be as the following

/tmp/script.bash
hello world , we are very happy to stay here

and

# more /tmp/file.txt
hello world , we are very happy to stay here
# more /tmp/sec_file.txt
hello world , we are very happy to stay here
2 Answers

I think, the easiest is to just add multiple files as arguments to the tee like this:

% python3 -c 'import sys; print("to stdout"); print("to stderr", file=sys.stderr)' 2>&1 | tee -a /tmp/file.txt /tmp/file_sec.txt
to stdout
to stderr
% cat /tmp/file.txt 
to stdout
to stderr
% cat /tmp/file_sec.txt 
to stdout
to stderr

Your script would look like this then:

#!/bin/bash

file=/tmp/file.txt
sec_file=/tmp/sec_file.txt

exec > >(tee -a "$file" "$sec_file") 2>&1

echo "hello world , we are very happy to stay here "

I would suggest to just write console things to a new output channel:

#!/bin/bash
file=file.txt
sec_file=sec_file.txt

exec 4>&1 > >(tee -a "$file" >>"$sec_file") 2>&1

echo "stdout"
echo "stderr" >&2
echo "to the console" >&4

Output:

me@pc:~/⟫ ./script.sh 
to the console
me@pc:~/⟫ cat file.txt 
stdout
stderr
me@pc:~/⟫ cat sec_file.txt 
stdout
stderr

If you want you can do this and even write to stderr again with >&5:

exec 4>&1 5>&1 > >(tee -a "$file" >>"$sec_file") 2>&1
echo "stderr to console" >&5

Edit: Changed &3 to &4 as &3 is sometimes used for stdin.

But maybe this is the moment to rethink what you are doing and keep &1 stdout and &2 stderr and use &4 and &5 to write to file?

exec 4> >(tee -a "$file" >>"$sec_file") 5>&1

This does require you though to add to all lines that should end up in your file to prepend >&4 2>&5

Related