Bash Redirection: stdin, stdout and stderr

Tested on RHEL 10.2 (Coughlan)
Package bash 5.2.26-6.el10
Applies to Linux distributions with Bash installed
Privilege Normal user; sudo -u nobody used for permission-denied examples
Scope Bash redirection for FD 0/1/2: <, >, >>, 2>, 2>>, >&2, 2>&1, &>/&>> (Bash), split destinations, /dev/null, brace groups, and exec script-wide redirects. Does not cover named/custom file descriptors, pipelines and pipefail, heredocs, or full logging architecture.
Related guides Bash command substitution
Bash shell expansions
Shell scripting course hub
Short on time?Beta Get the gist of this 11-minute read in seconds, then ask follow-up questions

Bash redirection connects a command's standard input, output, and error streams to files, devices, or other file descriptors. The operators look small, but Bash applies them left to right while updating descriptor state — so cmd >out.log 2>&1 and cmd 2>&1 >out.log are not the same command.

This guide uses one helper that prints a tagged stdout line and a tagged stderr line. Every example shows exactly what lands in the terminal and what lands in each file.


What Are stdin, stdout and stderr?

Commands normally inherit three standard file descriptors from the shell:

FD Name Typical interactive connection Typical use
0 stdin Terminal Command reads input
1 stdout Terminal Normal program output
2 stderr Terminal Errors and diagnostics

In a script or pipeline, stdin may come from a file, pipe, or socket instead of the terminal. Redirection changes where FD 0, 1, or 2 point before the command runs. Bash does not move bytes after the fact — it rewires the descriptors, then executes the command.

The helper used in this article writes one line to each stream:

bash
demo_io() {
  printf 'DATA: stdout line\n'
  printf 'DIAG: stderr line\n' >&2
}

Run it with no redirection to see the baseline — stdout and stderr both appear on the terminal:

bash
demo_io
output
DATA: stdout line
DIAG: stderr line

DATA: marks normal output; DIAG: marks diagnostics. The tags make it obvious which stream each line used.


Bash Redirection Quick Reference

Operator Effect
< file / 0< file Read stdin from file
> file / 1> file Redirect stdout to file (truncate)
>> file / 1>> file Redirect stdout to file (append)
2> file Redirect stderr to file (truncate)
2>> file Redirect stderr to file (append)
>&2 / 1>&2 Send stdout to the current stderr destination
2>&1 Duplicate FD 2 from whatever FD 1 points to right now
&> file / &>> file Bash: redirect stdout and stderr together (not POSIX sh)
>out 2>err Send stdout and stderr to different files

When the file descriptor is omitted before >, Bash uses FD 1 (stdout), so >file and 1>file are equivalent. The same rule applies to < and FD 0.

Bash processes redirections in source order. That order is the key to understanding 2>&1.


Redirect stdout with > and >>

> sends FD 1 to a file and truncates it first. >> appends instead.

Save only stdout while stderr still prints on the terminal:

bash
demo_io >stdout.log
output
DIAG: stderr line

Stdout went to the file; stderr was never redirected:

bash
cat stdout.log
output
DATA: stdout line

>> keeps prior file contents. Truncate the file first so the append demo starts from a known state — >> itself does not clear an existing file:

bash
: >append.log
demo_io >>append.log
demo_io >>append.log
output
DIAG: stderr line
DIAG: stderr line

Only the stdout lines were appended:

bash
cat append.log
output
DATA: stdout line
DATA: stdout line

Redirect stdin with <

< points FD 0 at a file. Use it when a command already reads stdin — there is no need to pipe from cat when the file can feed the loop directly:

bash
printf 'input line\n' > data.txt
while IFS= read -r line; do printf 'read=%s\n' "$line"; done < data.txt
output
read=input line

The same pattern appears in read a file line by line with while IFS= read -r loops.


Redirect stderr with 2> and 2>>

2> sends only FD 2 to a file. FD 1 stays on the terminal unless you redirect it separately.

Capture diagnostics while normal output still prints:

bash
demo_io 2>stderr.log
output
DATA: stdout line

Check what landed in the stderr file:

bash
cat stderr.log
output
DIAG: stderr line

2>> appends stderr the same way >> appends stdout. Start from an empty file again:

bash
: >diag.log
demo_io 2>>diag.log
demo_io 2>>diag.log
cat diag.log
output
DIAG: stderr line
DIAG: stderr line

Send stdout to stderr with >&2

>&2 is shorthand for 1>&2: make stdout (FD 1) write to the same destination as stderr (FD 2). Scripts use this when normal output should stay on stdout and warnings belong on stderr:

bash
printf 'Error: configuration missing\n' >&2
output
Error: configuration missing

That line appears on stderr (the terminal in an interactive shell), not in a redirected stdout file. The helper's printf 'DIAG: …\n' >&2 uses the same operator.


What Does 2>&1 Mean?

2>&1 makes FD 2 a duplicate of FD 1's current destination. It does not create a permanent link between them — later changes to FD 1 do not automatically move FD 2.

Before any redirection on an interactive terminal:

text
1 → terminal
2 → terminal

After >out.log:

text
1 → out.log
2 → terminal

After 2>&1 (with FD 1 already pointing at out.log):

text
1 → out.log
2 → out.log

If 2>&1 runs before >out.log, FD 1 still points at the terminal when the duplication happens. FD 2 is copied from that terminal connection. A later >out.log rewires only FD 1 — FD 2 keeps writing to the terminal.

That is why order matters.


Why Redirection Order Matters

Compare the two orderings side by side.

Both stdout and stderr go to the file:

bash
demo_io >combined.log 2>&1
cat combined.log
output
DATA: stdout line
DIAG: stderr line

Step by step:

  1. >combined.log connects FD 1 to combined.log.
  2. 2>&1 duplicates FD 2 from FD 1, so stderr also writes to combined.log.

Only stdout goes to the file:

bash
demo_io 2>&1 >stdout-only.log
output
DIAG: stderr line

Stdout reached the file; stderr stayed on the terminal:

bash
cat stdout-only.log
output
DATA: stdout line

Here 2>&1 ran while FD 1 still pointed at the terminal, so FD 2 was duplicated from the terminal. The later >stdout-only.log moved only FD 1 to the file.

When you need one log file, use >file 2>&1 (or &>file in Bash). When you need stderr on the terminal and stdout in a file, use >file alone.


Redirect stdout and stderr to One File

>>file 2>&1 appends both streams to the same file:

bash
: >combined_append.log
demo_io >>combined_append.log 2>&1
demo_io >>combined_append.log 2>&1
cat combined_append.log
output
DATA: stdout line
DIAG: stderr line
DATA: stdout line
DIAG: stderr line

Bash also provides &> and &>> as shorthand for >file 2>&1 and >>file 2>&1:

bash
demo_io &>amp.log
cat amp.log
output
DATA: stdout line
DIAG: stderr line

Append both streams on repeated runs:

bash
: >amp_append.log
demo_io &>>amp_append.log
demo_io &>>amp_append.log
cat amp_append.log
output
DATA: stdout line
DIAG: stderr line
DATA: stdout line
DIAG: stderr line

&> is a Bash extension. For POSIX sh scripts, use >file 2>&1 instead.


Send stdout and stderr to Different Files

Use separate redirection operators when data and diagnostics should land in different logs:

bash
demo_io >split.out 2>split.err
cat split.out
output
DATA: stdout line

The diagnostics file holds stderr only:

bash
cat split.err
output
DIAG: stderr line

Use >file 2>&1 when a single combined log is enough. Use >data 2>diag when downstream tools consume stdout and humans read stderr separately.


Discard Output with /dev/null

/dev/null accepts and discards any amount of data. Redirect a stream there when you want silence:

bash
demo_io >/dev/null 2>/dev/null
echo 'silent success'
output
silent success

Discard only stderr while keeping stdout:

bash
demo_io 2>/dev/null
output
DATA: stdout line

Normal > redirection sends a stream to the file instead of the terminal. When you need to save output and still see it on the screen, use tee through a pipeline — for example command | tee output.log, or command 2>&1 | tee output.log to include stderr. Pipeline exit-status handling is covered in redirection and pipes.


Redirect a Whole Command Group or Function

Curly braces group commands so one redirection applies to every command in the block. In a brace group, { and } must be separate shell tokens, and the command list must end with a semicolon or newline before }:

bash
{ demo_io; printf 'DATA: second stdout\n'; } >group.log 2>&1
cat group.log
output
DATA: stdout line
DIAG: stderr line
DATA: second stdout

The same syntax works on a function call:

bash
redirect_demo() {
  demo_io
  printf 'DATA: fn tail\n'
}
redirect_demo >fn.log 2>&1
cat fn.log
output
DATA: stdout line
DIAG: stderr line
DATA: fn tail

Redirect the Rest of a Script with exec

When exec is used with redirections but no command, the redirections are applied to the current shell. Commands run afterward inherit those changed file descriptors.

Run exec inside a throwaway subshell so you do not redirect your interactive session:

bash
bash -c 'exec >script.log 2>&1; demo_io() { printf "DATA: stdout line\n"; printf "DIAG: stderr line\n" >&2; }; demo_io; printf "after exec\n"'
cat script.log
output
DATA: stdout line
DIAG: stderr line
after exec

exec >file 2>&1 is the usual pattern for script-wide logging. Open the log once at the top; individual commands inside the script do not need their own > operators.


Why sudo command > file Can Still Fail

Redirection is parsed by the current shell before the command on the line runs. When you type:

bash
sudo printf 'root log\n' > /root/app.log

your shell — not sudo — tries to open /root/app.log for writing. If your user cannot create that file, the redirect fails before sudo starts.

Simulate that as an unprivileged user:

bash
sudo -u nobody bash -c 'printf "root log\n" > /root/app.log' 2>&1
output
bash: line 1: /root/app.log: Permission denied

printf never ran. The shell could not open the target file, so Bash reported the redirect error and exited with a non-zero status.

Put the redirect inside the elevated command instead:

bash
sudo bash -c 'printf "root log\n" > /root/app.log'
sudo cat /root/app.log
output
root log

A normal user usually cannot read /root directly, so use sudo for the verification step too.

Now the shell that sudo starts performs the redirection with root privileges. See the sudo command guide for elevation patterns; the redirect must live in the shell that has permission to open the file.


Common Redirection Mistakes

Symptom / trap Likely cause Fix
stderr missing from a "combined" log 2>&1 before >file Use >file 2>&1 or &>file
Permission denied with sudo cmd > /root/file Current shell opens the file sudo bash -c 'cmd > /root/file'
2>file behaves differently from 2 >file With a space, 2 becomes a command argument and > redirects stdout Keep the FD attached to the operator: 2>file
Output corrupted when using >file 2>file The same file is opened separately for FD 1 and FD 2 Use >file 2>&1
Command never runs, redirect error only Shell cannot open target file Fix path, permissions, or parent directory
&> fails under sh Bash-only operator Use >file 2>&1 for POSIX shells
Empty file after redirect Command wrote only to stderr Add 2>&1 or redirect stderr separately
{ cmd parse error { not a separate token or missing ; before } { cmd; } — {/} separated from words; list ends with ; or newline before }

A failed redirection can prevent the command from running at all. Bash opens redirection targets before executing the command; if opening fails, you see the shell error and the command does not start.


Summary

Bash redirection rewires file descriptors 0, 1, and 2 before a command runs. Use > and >> for stdout, 2> and 2>> for stderr, >&2 to send stdout to stderr, and < to feed stdin from a file. The operator 2>&1 duplicates FD 2 from FD 1 at the moment it is parsed — so cmd >file 2>&1 sends both streams to file, while cmd 2>&1 >file leaves stderr on the terminal.

Bash &> and &>> combine both streams in one step. /dev/null discards output you do not need. Use tee when output must appear on the terminal and in a file at the same time. Brace groups and functions accept a single redirection for the whole block, and exec >log 2>&1 redirects the rest of a script.

Remember that your shell performs redirection before sudo runs. Put > /root/file inside the elevated bash -c '…' when root must own the open file.


References


Frequently Asked Questions

What does 2>&1 mean?

It makes FD 2 a duplicate of FD 1's current destination at that point in the command line. It does not automatically mean "stderr goes to the stdout file" unless FD 1 already points there.

Why does 2>&1 >file behave differently from >file 2>&1?

Bash applies redirections left to right. In 2>&1 >file, stderr is duplicated from the terminal-bound FD 1 before stdout is moved to file. In >file 2>&1, stdout goes to file first, then stderr is duplicated from that connection.

How do I redirect stderr only?

Use 2>file or 2>>file. FD 1 stays on the terminal unless you redirect it separately with > or >>.

How do I append both stdout and stderr?

Use >>file 2>&1 or Bash &>>file. The append operator must open the file before 2>&1 duplicates FD 2 from FD 1.

Why can redirection fail even with sudo?

The current shell opens redirection targets before the command runs. In sudo cmd > /root/file, your shell tries to create /root/file — not sudo. Put the redirect inside the elevated shell: sudo bash -c 'cmd > /root/file 2>&1'.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)