Linux Input, Output Redirection and Pipes

Tested on RHEL 10.2 (Coughlan)
Package bash 5.2.26-6.el10
coreutils 9.5-8.el10_2
Applies to Ubuntu, Debian, Kali Linux, Linux Mint, Pop!_OS, Raspberry Pi OS, elementary OS, Zorin OS, Parrot OS, MX Linux, RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Arch Linux
Privilege Normal user for most examples; sudo when writing protected paths with tee
Scope File descriptors 0, 1, and 2; output and input redirection; stderr merging and ordering; pipes and multi-stage pipelines; tee, /dev/null, here documents and here strings; pipeline exit status with $?, PIPESTATUS, and pipefail. Does not cover process substitution, named pipes in depth, or full shell scripting.
Related guides Linux command line
Edit text files

Redirection symbols look cryptic until you see them as file descriptors the shell rewires before a command runs. Once stdout, stderr, and stdin are separate channels in your head, 2>&1, pipes, and tee stop feeling like magic punctuation.


What Are stdin, stdout, and stderr?

Every command the shell starts inherits three standard streams. The kernel numbers them as file descriptors so the shell can redirect them independently.

Stream File descriptor Typical use
stdin 0 Keyboard input, pipe, or file fed into the command
stdout 1 Normal program output
stderr 2 Errors and diagnostics

ls prints matching paths on stdout and complaints on stderr. Run it against one real file and one missing path so you can see both streams on the terminal.

bash
ls /etc/hosts missing-file-xyz

Sample output:

output
ls: cannot access 'missing-file-xyz': No such file or directory
/etc/hosts

The error line came from stderr (descriptor 2). The path line came from stdout (descriptor 1). Order on the terminal can vary, but the split is what matters when you redirect one stream and leave the other visible.


Linux Redirection Quick Reference

Keep this table nearby while you practice. The sections below walk through each row with real output.

Syntax Meaning
> file Replace file with stdout
>> file Append stdout
< file Read stdin from file
2> file Redirect stderr
2>> file Append stderr
> file 2>&1 Send stdout and stderr to the same file
2>&1 > file Different result because of evaluation order
command1 | command2 Pipe stdout of left command to stdin of right
command |& other Pipe stdout and stderr (Bash)
command | tee file Display and save stdout
> /dev/null Discard stdout

Redirect Standard Output

The > operator connects file descriptor 1 to a file. The shell creates or truncates that file before the command runs, so a careless redirect can wipe existing content.

Work in /tmp so examples stay easy to clean up. Create a fresh file with a single line using echo and redirection.

bash
cd /tmp && echo 'line one' > out.txt

Read the file to confirm stdout landed on disk.

bash
cat /tmp/out.txt

Sample output:

output
line one

To see why > is destructive, write one string, redirect again with a different string, and inspect the file.

bash
echo 'original' > /tmp/trunc.txt
bash
echo 'replaced' > /tmp/trunc.txt
bash
cat /tmp/trunc.txt

Sample output:

output
replaced

Only the second string remains. Treat > like "start this file over" unless you intentionally want to discard what was there.


Append Output to a File

Use >> when you want to add lines without truncating the file. The first redirect creates the file; the second appends.

bash
echo 'first' > /tmp/append.txt
bash
echo 'second' >> /tmp/append.txt
bash
cat /tmp/append.txt

Sample output:

output
first
second

Repeated logging and cron jobs usually use >> so each run adds a line instead of erasing the log.


Redirect Standard Error

Descriptor 2> sends stderr to a file while stdout can still print on the terminal. Ask ls for a path that does not exist and capture only the error.

bash
ls missing-err-xyz 2> /tmp/err.log

stderr no longer appears on the terminal. Inspect the error file.

bash
cat /tmp/err.log

Sample output:

output
ls: cannot access 'missing-err-xyz': No such file or directory

When you redirect stdout alone, stderr still appears on the terminal. That surprises people who expect silence after >.

bash
ls /etc/hosts missing-only-stdout-xyz > /tmp/out-only.txt

Sample output:

output
ls: cannot access 'missing-only-stdout-xyz': No such file or directory

The file holds only the successful stdout line.

bash
cat /tmp/out-only.txt

Sample output:

output
/etc/hosts

Redirect stdout and stderr Separately

Split streams when you want a clean success log and a separate error log. Both redirections apply to the same command; order between > and 2> does not change the outcome here because each targets its own descriptor.

bash
ls /etc/hosts missing-sep-xyz > /tmp/out.log 2> /tmp/err.log

Check stdout first.

bash
cat /tmp/out.log

Sample output:

output
/etc/hosts

Then stderr.

bash
cat /tmp/err.log

Sample output:

output
ls: cannot access 'missing-sep-xyz': No such file or directory

Automation that emails operators only on failure often keeps this split: stdout in a result file, stderr in an alert file.


Redirect stdout and stderr to the Same File

To merge both streams into one file, point stderr at wherever stdout already goes using 2>&1. The 2>&1 token means "make descriptor 2 a copy of descriptor 1," not "send stderr to a file named &1."

bash
ls /etc/hosts missing-same-xyz > /tmp/all.log 2>&1
bash
cat /tmp/all.log

Sample output:

output
ls: cannot access 'missing-same-xyz': No such file or directory
/etc/hosts

Bash also offers &> as shorthand for the same merge when you do not need separate steps.

bash
ls /etc/hosts missing-amp-xyz &> /tmp/amp.log
bash
cat /tmp/amp.log

Sample output:

output
ls: cannot access 'missing-amp-xyz': No such file or directory
/etc/hosts

Both forms capture errors and normal output in one place for later review.


Why Redirection Order Matters

The shell applies redirections left to right. That detail explains why > file 2>&1 and 2>&1 > file behave differently even though they look similar.

Correct order: open the file for stdout first, then duplicate stderr onto that same destination.

bash
ls /etc/hosts missing-wrong-xyz > /tmp/order-good.log 2>&1
bash
cat /tmp/order-good.log

Sample output:

output
ls: cannot access 'missing-wrong-xyz': No such file or directory
/etc/hosts

Wrong order: stderr is duplicated to the terminal's stdout first, then stdout alone moves to the file.

bash
ls /etc/hosts missing-wrong-xyz 2>&1 > /tmp/order-bad.log

Sample output:

output
ls: cannot access 'missing-wrong-xyz': No such file or directory

Only stdout went to the file; stderr still hit the terminal.

bash
cat /tmp/order-bad.log

Sample output:

output
/etc/hosts

Walk through the wrong case in order: 2>&1 copies stderr onto the current stdout (your terminal). Then > order-bad.log moves stdout to the file, leaving stderr on the terminal. Always place 2>&1 after the redirect that defines where stdout should go.


Redirect Standard Input

The < operator feeds a file to stdin (descriptor 0). Word count is a clear example because it reads from stdin when you do not pass a filename.

bash
wc -l < /etc/hosts

Sample output:

output
9

Many commands accept a filename argument directly, so < is optional for them.

bash
wc -l /etc/hosts

Sample output:

output
9 /etc/hosts

Use < when the command only reads stdin, when you build pipelines, or when you want the same script to read from a file or a pipe without changing the command name.


Use Pipes to Connect Commands

A pipe | connects stdout of the command on the left to stdin of the command on the right. stderr is not included unless you redirect it separately or use Bash |&.

Sort lines fed from printf to show the data flow.

bash
printf 'b\na\nc\n' | sort

Sample output:

output
a
b
c

printf wrote to stdout; sort read those lines from stdin and wrote sorted lines to stdout.

Filter running processes with grep the same way.

bash
ps aux | grep '[s]shd' | head -2

Sample output:

output
root        1193  0.0  0.1   9160  6684 ?        Ss   22:42   0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root        2218  0.0  0.1  15680 10708 ?        Ss   22:43   0:00 sshd-session: root [priv]

Search recent journal lines through a pipe when you want live filtering without saving the full log first.

bash
journalctl -u sshd --no-pager -n 3 | grep -i sshd | head -2

Sample output:

output
Aug 07 22:47:49 vm1.lab.example sshd-session[3832]: pam_unix(sshd:session): session opened for user root(uid=0) by root(uid=0)
Aug 07 22:47:50 vm1.lab.example sshd-session[3836]: Accepted password for root from 10.0.2.2 port 65440 ssh2

If a pipeline must forward stderr as well, Bash |& pipes both streams. This example counts every line ls prints, including its error message.

bash
ls /etc/hosts missing-pipe-xyz |& wc -l

Sample output:

output
2

Build Multi-Stage Pipelines

Long pipelines are easier to read when you know what each stage consumes and produces. Start with two words on stdin, keep only lines that match ok, then count how many survived.

bash
printf 'error line\nok line\n' | grep ok | wc -l

Sample output:

output
1

After printf, stdout held two lines. grep ok passed one line forward. wc -l read that single line and printed the count. Add a third stage only when each step still has a clear input and output; unreadable one-liners hide failures in the middle.


Save and Display Output with tee

tee copies stdin to stdout and to a file, so you watch output live while you save a copy. See the tee command for -a and other flags; here we focus on redirection patterns.

bash
printf 'visible\n' | tee /tmp/tee-demo.txt

Sample output:

output
visible

The same text is on disk.

bash
cat /tmp/tee-demo.txt

Sample output:

output
visible

Append with -a when the file should keep prior content.

bash
printf 'more\n' | tee -a /tmp/tee-demo.txt
bash
cat /tmp/tee-demo.txt

Sample output:

output
visible
more

Shell redirection runs as your user before an elevated command starts. sudo echo text > /etc/protected.conf fails for a normal user because the shell opens /etc/protected.conf without root privileges. Pipe to tee so the program that opens the file runs under sudo.

bash
printf 'admin note\n' | sudo tee /etc/redir-demo.conf
bash
cat /etc/redir-demo.conf

Sample output:

output
admin note

tee opened the path with root privileges while still printing the line to your terminal.


Discard Output with /dev/null

/dev/null is a special device that accepts writes and discards them. It is the standard way to silence a stream without leaving an empty file to clean up.

Silence stderr when you only care whether a command succeeded.

bash
ls missing-null-xyz 2>/dev/null

The command exits with no stderr line on the terminal.

Discard stdout while keeping stderr visible.

bash
ls /etc/hosts > /dev/null

Nothing is printed because stdout was redirected to /dev/null.

Send both streams away when neither is useful.

bash
ls /etc/hosts missing-both-xyz > /dev/null 2>&1

The command runs quietly; check $? if you need the exit status.


Redirect Input with Here Documents and Here Strings

Here documents and here strings feed stdin without a separate file on disk. They are common in scripts; this section covers the syntax only.

A here document with a quoted delimiter disables expansion inside the block.

bash
cat << 'EOF' > /tmp/heredoc.txt
line a
line b
EOF
bash
cat /tmp/heredoc.txt

Sample output:

output
line a
line b

A here string passes one string on stdin. <<< is a Bash extension.

bash
grep -F hosts <<< 'search hosts in this string'

Sample output:

output
search hosts in this string

For loops, functions, and larger scripts, continue with Bash shell scripting rather than growing this lesson into a scripting manual.


Pipeline Exit Status

$? holds the exit status of the last command. In a pipeline, Bash normally reports the status of the rightmost command unless you change shell options.

Run a failing command piped to a succeeding one and inspect $?.

bash
false | true
bash
echo $?

Sample output:

output
0

The pipeline looks successful even though false failed. Check every stage with PIPESTATUS.

bash
false | true
bash
echo PIPESTATUS:${PIPESTATUS[@]}

Sample output:

output
PIPESTATUS:1 0

Enable pipefail so a failure in an earlier pipeline stage can make the whole pipeline non-zero. The returned status is from the rightmost command that exited non-zero. Bash still waits for every stage in the pipeline; pipefail does not make the shell fail fast mid-pipe.

bash
set -o pipefail
bash
false | true
bash
echo $?

Sample output:

output
1

Use pipefail in scripts that grep or filter logs so a broken upstream command does not masquerade as success.


Common Redirection Mistakes

Symptom Likely cause Fix
File emptied after redirect > truncates before the command runs Use >> to append, or copy the file first
Errors still on screen Only stdout was redirected Add 2> file, 2>&1, or &>
Permission denied with sudo echo > /etc/file Shell opens the file as your user Pipe to sudo tee /path/file
stderr missing from combined log Used 2>&1 > file instead of > file 2>&1 Put the stdout redirect first, then 2>&1
Pipeline "succeeds" but data wrong Earlier stage failed silently Check PIPESTATUS or set -o pipefail
Unexpected words in output Unquoted redirection target expanded Quote paths and use << 'EOF' when literals matter

Practical Examples

These patterns mirror day-to-day administration: capture output, split errors, search logs, save filtered results, and drop noise.

Capture check-update text in one file and errors in another.

bash
dnf check-update --quiet 2> /tmp/dnf-err.log > /tmp/dnf-out.log

dnf check-update returns 100 when updates are available, 0 when none are available, and 1 on error, so status 100 is not a command failure.

bash
wc -l /tmp/dnf-out.log /tmp/dnf-err.log

Sample output:

output
60 /tmp/dnf-out.log
   0 /tmp/dnf-err.log
  60 total

Search a saved log through grep without re-running the heavy command.

bash
grep -i error /tmp/dnf-out.log | head -3

Save filtered journal lines while watching them live.

bash
journalctl -u sshd --no-pager -n 5 | tee /tmp/sshd-recent.log | grep -i accepted

Hide benign "file not found" noise from a script loop.

bash
find /etc -name 'redir-demo.conf' 2>/dev/null

Sample output:

output
/etc/redir-demo.conf

References


Summary

You started with three standard streams—stdin, stdout, and stderr—and saw how file descriptors 0, 1, and 2 let the shell redirect them independently. Redirecting stdout with > or >>, stderr with 2>, and both with > file 2>&1 or &> gives you precise control over what lands on the terminal, in a log file, or in the bit bucket.

The ordering lesson is the one most tutorials skip: 2>&1 duplicates stderr onto whatever stdout currently uses, so > file must come before 2>&1 when you want both streams in the same file. Pipes wire stdout to stdin on the next command; stderr stays behind unless you pipe it with |& or merge it first. tee solves the sudo problem by letting the elevated program open the protected path while you still see output live.

Pipeline exit status is the other hidden footgun: without pipefail, a failed grep or curl early in the chain can leave $? at zero because only the last command counts. Check PIPESTATUS while debugging, then enable pipefail in scripts that need to detect failures anywhere in a pipeline.


Frequently Asked Questions

1. What is the difference between stdout and stderr?

stdout carries normal program output on file descriptor 1. stderr carries errors and diagnostics on file descriptor 2. They are separate streams, so redirecting stdout does not hide stderr unless you redirect or merge descriptor 2 as well.

2. Why does command > file 2>&1 work but 2>&1 > file fails to capture stderr?

The shell applies redirections left to right. In > file 2>&1, stdout goes to the file first, then stderr is pointed at whatever stdout currently uses. In 2>&1 > file, stderr is duplicated to the original stdout before stdout is moved to the file, so errors still print on the terminal.

3. Does a pipe carry stderr to the next command?

A normal pipe connects stdout of the left command to stdin of the right command. stderr is not piped unless you use Bash |& or redirect stderr into the pipe yourself.

4. Why does sudo echo text > /etc/file fail for a normal user?

Your shell opens and truncates the target file before sudo runs the command. Redirection is handled by the shell with your user permissions, not by the elevated command. Pipe the text to sudo tee instead so tee opens the file as root.

5. What does redirecting to /dev/null do?

/dev/null is a device that accepts any amount of data and discards it. Redirecting stdout or stderr there silences that stream without creating a file on disk.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 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)