Skip to content
shellmap

Merge stderr into stdout

Combine a command's error stream with its normal output so a single pipe / file / variable captures BOTH — essential for log aggregation and CI capture.

How to merge stderr into stdout in each shell

Bashunix
./build.sh 2>&1 | tee build.log

`2>&1` = "duplicate fd 2 (stderr) onto fd 1 (stdout)". Read RIGHT-TO-LEFT: stderr → wherever stdout is GOING. Must come BEFORE `| pipe` for the pipe to see merged streams.

Zshunix
./build.sh 2>&1 | tee build.log
Fishunix
./build.sh 2>&1 | tee build.log

Fish supports the same `2>&1` syntax as bash (added in fish 3.0, 2018). Earlier fish needed `./build.sh ^&1 | ...` — the old caret syntax is now removed (fish 3.4+).

PowerShellwindows
.\build.ps1 *>&1 | Tee-Object -FilePath build.log

`*>&1` merges ALL six pwsh streams (Success/Error/Warning/Verbose/Debug/Information) onto stdout. For stderr-only `2>&1`. For native exes inside pwsh, the exe's own stderr maps to pwsh stream 2 — `nativeexe 2>&1` works as expected.

cmd.exewindows
build.cmd > build.log 2>&1

cmd uses the same `2>&1` redirection. Must come AFTER `> build.log` (cmd reads left-to-right when opening fds — opposite of bash which reads right-to-left).

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • **Redirect-ordering trap (same characters, different semantics)**: `cmd >file 2>&1` redirects BOTH stdout and stderr to `file`. `cmd 2>&1 >file` keeps stderr on the CONSOLE (only stdout goes to the file). The reason: `>file` REASSIGNS fd 1 to the file; `2>&1` makes fd 2 a DUPLICATE OF WHATEVER fd 1 POINTS TO RIGHT NOW. In `>file 2>&1` order, fd 1 first points at the file, then fd 2 is duplicated FROM that → both at file. In `2>&1 >file` order, fd 2 is first duplicated from the CONSOLE-pointing fd 1, then fd 1 is reassigned to the file — fd 2 still points at the console. This is one of the most-frequently-asked questions on Unix StackExchange; the rule is "redirect THEN duplicate, never the reverse".
  • `|&` (bash 4 / zsh) is shorthand for `2>&1 |` — `cmd |& tee file` is equivalent to `cmd 2>&1 | tee file` but more readable. POSIX `sh` does NOT support `|&` — write `2>&1 |` for portable scripts. fish does support `|&` (added fish 3.0). pwsh does NOT support `|&` — use `*>&1 |`. cmd does NOT support `|&` either — use the long-form `2>&1 |`.
  • pwsh has SIX streams (not just two): **1** Success (stdout), **2** Error (stderr), **3** Warning, **4** Verbose, **5** Debug, **6** Information. Redirect each: `cmd 3>warn.log 4>verbose.log`. Merge any onto stdout: `cmd 3>&1 4>&1 5>&1 6>&1` (or the wildcard `*>&1` does all five non-stdout streams at once). NATIVE exes inside pwsh only see streams 1 and 2 (they're POSIX processes); the cmdlet streams 3-6 are pwsh-internal. So `nativeexe.exe *>&1` is equivalent to `nativeexe.exe 2>&1`.
  • Variable capture in bash: `out=$(cmd 2>&1)` captures both streams into `$out`; without the `2>&1` stderr still prints to terminal AND `$out` only gets stdout. Common need: capture both, check exit code, log on failure: `out=$(cmd 2>&1); rc=$?; [ $rc -ne 0 ] && echo "$out" >&2`. In pwsh: `$out = & cmd *>&1` then `if ($LASTEXITCODE) { Write-Error $out }`. In fish: `set out (cmd 2>&1)` works since fish 3.0. In cmd: there is no equivalent — output capture is `for /f` which only sees stdout, and there's no way to merge stderr into the captured value without shelling out to pwsh.

Related commands

Related tasks