← Blog

Understand Bash stderr-to-stdout redirection before changing production logs

Trace file descriptors in order, verify combined streams safely, preserve pipeline failures, and recover when redirection hides evidence.

Linux & Server OperationsBy Emmanuel Corels

2>&1 is only five characters, but a wrong mental model can hide a failed backup, discard diagnostics, or send secrets into the wrong log. It does not permanently “merge” two streams. It changes where file descriptor 2 points at one precise moment while the shell prepares a command.

Read the notation from left to right

A process conventionally starts with descriptor 0 for standard input, 1 for standard output, and 2 for standard error. In 2>&1, the first 2 selects standard error, > is output redirection, and &1 means “the destination currently referenced by descriptor 1.” Without the ampersand, 2>1 opens a file literally named 1.

command >run.log 2>&1

The shell first opens run.log for descriptor 1, then duplicates that destination onto descriptor 2. Both streams therefore go to the same open file description. The command itself receives already-configured descriptors.

Order is part of the program

Redirections are processed in the order written. These commands are not equivalent:

# stdout to the file, then stderr to stdout's new destination
command >run.log 2>&1

# stderr copies stdout's original destination, then stdout moves to the file
command 2>&1 >run.log

In the second form, errors still appear on the original terminal while normal output enters the file. Diagnose a surprising result by expanding the line into those two ordered operations. Bash also supports &>run.log and >&run.log for redirecting both streams, but the Bash manual recommends the first form; >run.log 2>&1 remains clearer when portability to other POSIX-like shells matters.

Build a harmless verification probe

Do not test a new logging expression on a production backup or destructive job. Use a command that deliberately writes one line to each descriptor:

sh -c 'printf "normal\n"; printf "problem\n" >&2' \
  >combined.log 2>&1

wc -l combined.log
sed -n '1,2p' combined.log

The file should contain exactly two lines. Then test separation:

sh -c 'printf "normal\n"; printf "problem\n" >&2' \
  >stdout.log 2>stderr.log

Each file should contain its corresponding line. Test under the actual interpreter used by cron, systemd, CI, or a container entrypoint; syntax accepted by interactive Bash may not be accepted by /bin/sh on every host.

Pipelines change what downstream tools see

A pipe normally connects only descriptor 1 of the left command. Therefore command | head limits ordinary output while errors continue to the terminal. Placing 2>&1 before the pipe makes both streams enter head:

command 2>&1 | head -n 20

That can be useful during diagnosis, but it also loses stream identity and can terminate the producer early when head closes the pipe. With Bash, enable and inspect pipefail when the pipeline’s overall status must expose an earlier failure:

set -o pipefail
command 2>&1 | tee run.log
status=$?

Capture the status immediately. A later command overwrites $?. For Bash-specific diagnosis, PIPESTATUS records each pipeline component, but it too must be read before another command changes it.

Production failure modes

  • Silent truncation: >run.log truncates an existing file before the process starts. Use a new run-specific path, controlled rotation, or >> only when append semantics are intended.
  • Secret leakage: stderr can contain arguments, paths, tokens, or record fragments. Set restrictive ownership and modes, redact at the application, and define retention.
  • Interleaved records: two descriptors sharing a destination do not guarantee application-level ordering or atomic multi-line writes. Prefer structured logging with timestamps and request IDs.
  • Missing parent directory: the shell fails the redirection before launching the command. Validate the destination and surface the launcher’s error.
  • Wrong user or full filesystem: permission and capacity failures can erase the very evidence needed for recovery. Monitor log-write failures and filesystem usage.
  • Wrapper changes semantics: nested shells, service managers, and CI runners may add their own capture. Inspect the final executed command and the service’s logging configuration.

Rollback and recovery

If a rollout hides errors, first restore the last known logging configuration; do not rerun a state-changing job merely to recover its output. Preserve terminal capture, journal records, CI logs, and application telemetry. If > truncated the only log, stop writers to that filesystem and follow the storage recovery process—continued writes reduce recovery chances.

Roll out a redirection change to one canary task. Confirm destination ownership, line counts, exit status, rotation, alert ingestion, and behavior during forced failure. A useful acceptance test proves that success output arrives, deliberate stderr arrives, a non-zero exit remains non-zero, and credentials do not appear.

Attribution and current verification

This guide was prompted by Tristan Havelick’s Stack Overflow question and Ayman Hourieh’s accepted answer, used under CC BY-SA. The descriptor explanation and left-to-right behavior were independently verified against the current GNU Bash Redirections manual.

Primary source: Review the official reference ↗