0

I have some script with STDOUT and STDERR output. By default, both STDOUT and STDERR goes to console:

./script.sh

I can redirect STDOUT to file, leaving STDERR output in console:

./script.sh > log.txt

I can redirect both STDOUT + STDERR to same file with no output in console:

./script.sh > log.txt 2>&1

I can have both STDOUT and STDERR in console and logfile simultaneously:

./script.sh 2>&1 | tee -a log.txt

I don't want to see STDOUT, but I want to see STDERR, and I have to save everything in logfile. How could I have STDERR in console + logfile, while STDOUT in logfile only? Something like (pseudo-code, doesn't work):

./script.sh > log.txt 2 > /dev/tty | tee -a log.txt
AntonioK
  • 581
  • 2
  • 9
  • 25
  • Maybe something like will work: `./script.sh > log.txt | tee -a log.txt` – Romeo Ninov Aug 22 '23 at 06:24
  • 1
    @RomeoNinov: With that, the `> log.txt` would send the STDOUT to the file, STDERR left to STDERR. There's nothing left for the `tee` to handle. Therefore, it appends nothing to the file. You could confirm that easily with `./script.sh > log.txt | tee -a log2.txt`, which leaves the `log2.txt` empty. – Esa Jokinen Aug 22 '23 at 06:36
  • @EsaJokinen, STDERR by default go to console. But to be sure the command can be corrected on this way: `./script.sh 2>&1 > log.txt | tee -a log.txt`. This way STDERR will go to STDOUT, STDOUT will go to file w/o STDERR. – Romeo Ninov Aug 22 '23 at 06:47
  • You would still need to use `>>` instead of `>`. With your current suggestion, if anything comes from STDOUT after something was already appended from STDERR, it would overwrite the file. That's why you absolutely need to append both streams, as explained in my answer. Even then, the order of the rows gets scrambled, because `tee` is separated & slower than `>>`. – Esa Jokinen Aug 22 '23 at 07:12
  • @EsaJokinen, subshell + `tee` will be also slower than `>>` About need of `>>` maybe. – Romeo Ninov Aug 22 '23 at 08:46

1 Answers1

4

You would have to append both STDERR & STDOUT to the file. Otherwise the other of the streams would write over the other.

./script.sh >> log.txt 2> >(tee -a log.txt >&2)

Where:

  1. >> log.txt appends STDOUT to log.txt.
  2. 2> > sends the STDERR to to be processed by (tee -a log.txt >&2).
  3. tee -a log.txt splits that to the file log.txt in append (-a) mode and to >&2.
  4. As the STDERR was temporarily sent to STDOUT, the >&2 puts it back to STDERR.

Considerations

Because of the appending, if you run this twice, you would have the output twice in the file – which I suppose is the goal, given it is a log file. If you would like to have only the latest run in the log file, you would have to empty it first, e.g.,

> log.txt && ./script.sh >> log.txt 2> >(tee -a log.txt >&2)

The lines would not appear in chronological order, as >> works a bit faster tee -a. Because the latter has to fork another process, the output from STDERR would come to the file with a delay. If the correct order is critical, you cannot use this solution.

Esa Jokinen
  • 46,944
  • 3
  • 83
  • 129