How to make tee catch the stderr only in Linux?

Redirecting stderr to tee

The tee command by default reads from stdin and writes to both stdout and files. To capture only stderr, you need to explicitly redirect it.

Basic stderr redirection to tee

The straightforward approach is to redirect stderr to stdout, then pipe to tee:

command 2>&1 | tee output.log

This captures both stdout and stderr. If you want only stderr:

command 2>&1 1>/dev/null | tee error.log

This works by:

  1. 2>&1 — redirects stderr to stdout
  2. 1>/dev/null — discards the original stdout
  3. | tee error.log — captures only stderr to the file

Using process substitution for cleaner syntax

A more readable approach uses process substitution:

command 2> >(tee error.log)

This syntax:

  • 2> redirects stderr to a process
  • >(tee error.log) creates a process that receives stderr and writes it to both the terminal and error.log

The advantage here is clarity — it’s immediately obvious you’re capturing stderr.

Capturing both streams separately

If you need stdout and stderr in different files simultaneously:

command > >(tee stdout.log) 2> >(tee stderr.log)

Both streams display on the terminal and write to their respective files.

Combining with grep or other filters

You can pipe stderr through filters before tee:

command 2>&1 1>/dev/null | grep "ERROR" | tee errors-only.log

This captures only lines containing “ERROR” from stderr.

Real-world example: capturing deployment logs

./deploy.sh 2> >(tee deploy-errors.log >&2) | tee deploy-full.log

This:

  • Captures stderr to deploy-errors.log while keeping it on stderr (>&2)
  • Captures stdout to deploy-full.log
  • Displays both streams on the terminal

The >&2 at the end ensures stderr goes back to the terminal after being teed.

Appending instead of overwriting

Use the -a flag to append rather than overwrite:

command 2> >(tee -a error.log)

Suppressing terminal output while saving to file

If you only want the file, not the terminal output:

command 2> >(tee error.log >/dev/null)

The inner >/dev/null discards tee’s terminal output; stderr is still written to the file.

Combining with systemd services

For systemd services, capture stderr/stdout through journalctl:

journalctl -u service-name -f | tee service.log

This follows live logs and saves them to a file. For persistent capture, configure the service file:

[Service]
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myservice

Then logs appear in journalctl with your identifier tag.

Performance considerations

Process substitution adds a small file descriptor overhead. For high-frequency logging (thousands of lines/second), consider:

command 2>>error.log 1>>stdout.log

Direct redirection has less overhead than pipes, though you lose real-time terminal visibility.

Similar Posts

One Comment

  1. Thanks, it really helped.

    I’ve tried to make logging of STDOUT and STDERR to separate files, but also have STDERR output in the terminal. This is what I came up with.

    test-script.sh:
    “`
    #!/bin/bash

    echo ‘Regular stdout’
    1>&2 echo ‘Error!’
    “`

    Terminal:
    “`
    ./test-script.sh > file.log 2> >(tee file-error.log >&2)
    “`

    Output:
    “`
    Error!
    “`

    file.log:
    “`
    Regular stdout
    “`

    file-error.log:
    “`
    Error!
    “`

Leave a Reply

Your email address will not be published. Required fields are marked *