Redirect copy of stdout and stderr to one file, copy of just stderr to another file from within bash script - bash

Note that I have reviewed both stackoverflow questions below, but my question is different:
redirect COPY of stdout to log file from within bash script itself
redirect stdout and stderr to one file, copy of just stderr to another
In attempting to do this, I have the following test.sh script:
#!/bin/bash
rm stdout_and_stderr.log
rm stderr.log
exec 2> >(tee -ia stderr.log >> stdout_and_stderr.log) 1> >(tee -ia stdout_and_stderr.log)
echo "stdout"
echo "stderr" >&2
The only problem is that stderr is not displayed to terminal:
$ ./test.sh > /dev/null
$ ./test.sh 2> /dev/null
$ stdout
My version of bash:
GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu)

Here is a solution that works:
#!/bin/bash
rm stdout_and_stderr.log
rm stderr.log
exec 2> >(tee -ia stdout_and_stderr.log >&2)
exec 2> >(tee -ia stderr.log >&2)
exec 1> >(tee -ia stdout_and_stderr.log)
echo "stdout"
echo "stderr" >&2
It can also be done in one line:
exec 1> >(tee -ia stdout_and_stderr.log) 2> >(tee -ia stdout_and_stderr.log >&2) 2> >(tee -ia stderr.log >&2)

Related

Bash forwarding stdout, stderr and pipe to external program

I've a Bash command with runs a python script and produce outputs on stdout and stderror.
Normal and error logs are written to separate files with this command:
python3 file.py >> normal.log 2>> error.log
Additionally, stdout and stderr shall be forwarded to an external program (e.g. logToTelegram.sh):
python3 file.py 2>&1 | logToTelegram.sh
Is there a way to implement both during one execution, write log files (normal and error) and pipe stdout and stderror together to the program logToTelegram.sh?
This should do the trick:
( python3 file.py 2> >(tee -a error.log) > >(tee -a normal.log) ) | logToTelegram.sh
Note that 2> >(tee ...) has to be placed before > >(tee ...) for this to work correctly. For simplicity, both tee commands output to stdout, eliminating the need for 2>&1 before piping to logToTelegram.sh.
Or, to stay closer to the original code and to be more precise:
( python3 file.py > >(tee -a normal.log) 2> >(tee -a error.log >&2) ) 2>&1 | logToTelegram.sh
Here, the first tee outputs to stdout, while the second tee outputs to stderr, thus 2>&1 is required to send all output to logToTelegram.sh.

In a shell script, how to redirect stdout to console and both stdout and stderr to a file?

I need to redirect stdout to console along with stdout and stderr redirected to a file. This needs to be done inside a shell script.
I found the below code to redirect both to console and log file, now I need to remove stderr to console.
exec > >(tee -i "output.log") 2>&1
Could you please help me here?
Derived from this answer:
How do I get both STDOUT and STDERR to go to the terminal and a log file?
I tested the following code:
echo '' > out.log
exec 1> >(tee -a -i out.log) 2> >(tee -a -i out.log > /dev/null)
>&2 echo yay
echo nay
The STDERR content goes to file and not to console, and STDOUT goes to both.
I think you should launch it like this:
your-command 2>&1 | tee -i "output.log"
It will pipe both stdin and stderr to tee -i output.log, which will echo text before writing.
Or, to ignore stderr (pipe to /dev/null):
your-command 2>/dev/null | tee -i "output.log"
Or your command modified with /dev/null redirection:
exec > >(tee -i "output.log") 2>/dev/null
It'd be something like this, but you don't have to tee that second one.
exec 1> >( tee stdout.txt ) 2> stderr.txt
output 1 is tee'd so you see it on screen, and in stdout.txt
errors 2 just goes straight to stderr.txt

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

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
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

how to redirect stdout and stderr to a file while showing stderr to screen?

The script should redirect all the output (stdout and stderr) to a log file, and only display stderr to the screen (notifying user if an error happens). The command tee may help but don't know how to write it.
Thanks.
P.S., thanks lihao and konsolebox for the answer, but is there a way to keep the output in order. For example:
$ cat test.sh
echo "to stdout..1"
echo "to stderr..1" >&2
echo "to stdout..2"
echo "to stderr..2" >&2
$ sh test.sh 2>&1 >test.log | tee -a test.log
to stderr..1
to stderr..2
$ cat test.log
to stdout..1
to stdout..2
to stderr..1
to stderr..2
Command: { sh test.sh 2> >(tee /dev/fd/4); } 4>&1 >test.log has the same output.
how about the following:
cmd args 2>&1 >logfile | tee -a logfile
You should map normal stdout to another file descriptor (4), make the file the default output, then use tee to redirect output to the new file descriptor through /dev/fd. Of course you'd need process substitution to pass stderr output to tee:
{ cmd args 2> >(exec tee /dev/fd/4); } 4>&1 >file
If you want to make a general redirection for the script, place this at the beginning of it:
exec 4>&1 >file 2> >(exec tee /dev/fd/4)
You can restore normal output with:
exec >&4 4>&-

How to save STDERR and STDOUT of a pipeline on a file?

I'm running a pipeline of commands that have STDERR and STDOUT outputs. I want to save both outputs in a single log file.
This are my attempts to do it:
bash my_script.sh > log.txt #Only save STDOUT
bash my_script.sh > >(tee log.txt) 2> >(tee log.txt >&2) #The STDERR overwrite the STDOUT
I hope you can provide a simple solution to do this.
Thanks for your time!
How about just
bash my_script.sh > >(tee log.txt) 2>&1
Also if you want to append output if log.txt already exists, add -a option to tee
bash my_script.sh > >(tee -a log.txt) 2>&1
It's actually equivalent to bash my_script.sh 2>&1 | tee log.txt or bash my_script.sh 2>&1 | tee -a log.txt
bash my_script.sh > log.txt 2>&1
where 2>&1 redirects stderr to stdout

Resources