Tee command output to a file and stdout
Write a command's stdout to a file AND echo it to the terminal in one pass — for logged-but-watchable installer / build / CI runs.
How to tee command output to a file and stdout in each shell
./build.sh | tee build.logDefault mode TRUNCATES `build.log`. Use `-a` to append: `./build.sh | tee -a build.log`. To also capture stderr: `./build.sh 2>&1 | tee build.log` (REDIRECT BEFORE PIPE).
./build.sh | tee build.log./build.sh | tee build.log.\build.ps1 | Tee-Object -FilePath build.log`Tee-Object` aliases: `Tee-Object -Variable name` (also bind to a variable for in-script reuse). Append: `Tee-Object -FilePath build.log -Append`. Encoding: defaults to UTF-16 on pwsh 5.1; UTF-8 (no BOM) on pwsh 6+ — explicit: `-Encoding utf8NoBOM`.
build.cmd > build.log 2>&1 & type build.logcmd has NO native tee. Workarounds: (1) `> file 2>&1` then `type file` (sequential, no live output); (2) shell out to pwsh `Tee-Object`; (3) `wtee.exe` from Win32 ports if you have it.
Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.
Gotchas & notes
- **Why `tee` exists**: shell redirection `cmd > file` sends output ONLY to the file; the user sees nothing during long runs. `cmd | tee file` duplicates the stream — file gets the bytes, terminal gets the bytes. The portmanteau name comes from the "T" pipe-fitting shape. For interactive installers and CI runs you almost always want `| tee` over `> file` so users see progress AND a log is preserved.
- Capturing stderr with tee: `cmd | tee file` only sees STDOUT — stderr goes straight to the terminal, never the file. To capture both: `cmd 2>&1 | tee file` (the `2>&1` MERGES stderr into stdout BEFORE the pipe, so tee sees the unified stream). To capture them SEPARATELY (stderr in one file, stdout in another, both echo to terminal): `cmd > >(tee out.log) 2> >(tee err.log >&2)` (bash/zsh process substitution; doesn't work in fish/sh — see /task/use-process-substitution).
- Append vs truncate: bare `tee file` TRUNCATES `file` first (same as `>` redirect). `tee -a file` APPENDS. The same flag works on every Unix variant (POSIX standard since 2001). pwsh `Tee-Object -FilePath` truncates; `-Append` appends. cmd `>` truncates; `>>` appends — but cmd has no tee, so the only "append while echoing" idiom is the `>>file 2>&1 & type file` sequence (still not concurrent — file lags behind terminal until command finishes).
- Privileged file writes: `sudo cmd > /etc/protected.log` FAILS — the redirect runs in the user's shell BEFORE sudo, so the file is opened with USER permissions. Workaround: `cmd | sudo tee /etc/protected.log > /dev/null` — sudo runs `tee` with root permissions, tee opens the file, and we discard tee's stdout echo (`> /dev/null`) since we just want the file write. This is the canonical "echo something into /etc as root" idiom. pwsh equivalent: `cmd | Out-File -FilePath /etc/protected.log` (pwsh elevation is per-process, not per-command, so the whole pwsh session must be elevated).
Related commands
Related tasks
- Append command output to a file— Add a command's output to the END of a file (creating it if missing) without erasing existing content — for incremental logs, config patches, and accumulators.
- 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.
- Suppress stderr from a command— Discard a command's error output (without affecting stdout or exit code) — for noisy tools whose warnings clutter scripts and CI logs.