CrackOps
Interview PrepTracksLinuxAWS LabSoonInterview RoomSoon
Sign inStart free
CrackOps

Interview prep for the people who keep production up.

[email protected]
ProductInterview PrepTracksDashboardInterview Room
RolesDevOpsSREPlatformCloud
CompanyAboutContactPrivacyTerms
© CrackOps
Linux / Shell Scripting / bash: redirection

bash: redirection

Redirection: stdout, stderr, 2>&1, /dev/null and here-docs

Beginnerbashscriptingiologging

Every process starts with three open streams: stdin (0), stdout (1) and stderr (2). Redirection points them somewhere else before the command runs: > writes a file, >> appends, 2> captures errors, 2>&1 merges errors into output, < reads from a file, and here-documents feed a block of text in.

Syntaxcmd > file cmd >> file cmd 2> err cmd > file 2>&1 cmd &> file cmd < file cmd <<EOF ... EOF cmd <<< "text"

Watch it run

You save a command's output to a file, but the error still lands on your screen. See why, then capture each stream.

devops@devops-lab: ~ — zsh — 96×28
➜ ~
Step 1 of 4
ls ~/app /nope > out.txt

> redirected only fd 1. ls writes errors to fd 2, which still points at the terminal, so the error shows up and the listing goes to the file.

Anatomy of a command

$
command./deploy.sh prod

The program and its arguments. Redirections can appear anywhere on the line; bash removes them before running it.

How it works

A file descriptor is a small number indexing the process's table of open files. By convention 0 is stdin, 1 stdout, 2 stderr, and a terminal session starts with all three pointing at /dev/pts/N. For an external command bash forks a child, applies the redirections in that child from left to right, then execs the program, which simply writes to fd 1 and 2 without knowing where they lead. > file is open(file, O_WRONLY|O_CREAT|O_TRUNC) followed by dup2(fd, 1); >> uses O_APPEND instead of O_TRUNC; 2>&1 is dup2(1, 2), a copy of where fd 1 points at that moment. That is why > file 2>&1 sends both to the file but 2>&1 > file sends errors to the old stdout. Truncation happens before the command runs, so sort f > f empties f. A pipe | connects one command's fd 1 to the next one's fd 0; stderr skips the pipe unless you use |&. Here-documents are fed to fd 0 through a pipe or temp file.

bash rewires the child's file descriptors, left to right, before the program starts.

type lineforkwriteserrorsYoucmd >log 2>&1bashyour shellChild processafter fork()fd 1 stdout-> terminalfd 2 stderr-> terminallog fileopened O_TRUNCTerminal/dev/pts/0
1/6
bash parses the line

bash splits off the redirections > log and 2>&1. They are not arguments: the program will never see them.

← → to step

Options you'll actually use

> filestdout to file, truncating it first
>> filestdout appended to file
2> filestderr to file (2>> appends)
> file 2>&1stdout and stderr both to file (order matters)
&> fileBash shorthand for > file 2>&1 (&>> appends)
2>/dev/nullThrow error messages away
< filestdin from a file
cmd1 |& cmd2Pipe stdout and stderr (same as 2>&1 |)
<<EOF ... EOFHere-document: lines as stdin, $vars expanded
<<'EOF'Quoted delimiter: no expansion, text is literal
<<-EOFStrip leading tabs (not spaces) from the lines
<<< "$var"Here-string: one string as stdin
set -o noclobber, >|Refuse to overwrite existing files with > / force it once
exec >> log 2>&1Redirect the rest of the script's output

Common mistakes

cmd 2>&1 > out.log

Redirections apply left to right: stderr copied the terminal before stdout moved, so errors are not in the log.

cmd > out.log 2>&1 # or: cmd &> out.log
sort data.txt > data.txt

The shell truncates data.txt before sort reads it, leaving an empty file. Same with sed, grep, awk.

sort -o data.txt data.txt # or write to a temp file, then mv
sudo echo 'line' >> /etc/hosts

The redirection is done by your unprivileged shell, not by sudo.

echo 'line' | sudo tee -a /etc/hosts > /dev/null
cmd > /dev/null to silence a noisy command

Only stdout is discarded; errors still print. Sometimes you want that, often you don't.

cmd > /dev/null 2>&1 # or keep errors: cmd > /dev/null
cat <<EOF > run.sh with $1 and $(date) in the body

An unquoted delimiter expands them while writing the file, baking in today's values.

cat <<'EOF' > run.sh

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Run ls on ~/app and on a missing path, sending the normal output to ok.txt and the errors to err.txt. Check both files.

  2. 2

    Capture both stdout and stderr of the same ls into one file all.txt, and count its lines.

  3. 3

    Create ~/app/motd.txt with three lines using a here-document, where one line shows your user name via $USER.

  4. 4

    Append the line '127.0.0.1 lab.local' to /etc/hosts using sudo correctly.

Related commands

teeCopy a stream to the screen and to files at oncebash: exit codesExit codes, $?, set -euo pipefail and trapcatPrint and concatenate filescrontabSchedule recurring jobs with cronsudoRun a command as root or another user
Previousbash: exit codesNextbash: arguments
Linux / Shell Scripting / bash: redirection

bash: redirection

Redirection: stdout, stderr, 2>&1, /dev/null and here-docs

Beginnerbashscriptingiologging

Every process starts with three open streams: stdin (0), stdout (1) and stderr (2). Redirection points them somewhere else before the command runs: > writes a file, >> appends, 2> captures errors, 2>&1 merges errors into output, < reads from a file, and here-documents feed a block of text in.

Syntaxcmd > file cmd >> file cmd 2> err cmd > file 2>&1 cmd &> file cmd < file cmd <<EOF ... EOF cmd <<< "text"

Watch it run

You save a command's output to a file, but the error still lands on your screen. See why, then capture each stream.

devops@devops-lab: ~ — zsh — 96×28
➜ ~
Step 1 of 4
ls ~/app /nope > out.txt

> redirected only fd 1. ls writes errors to fd 2, which still points at the terminal, so the error shows up and the listing goes to the file.

Anatomy of a command

$
command./deploy.sh prod

The program and its arguments. Redirections can appear anywhere on the line; bash removes them before running it.

How it works

A file descriptor is a small number indexing the process's table of open files. By convention 0 is stdin, 1 stdout, 2 stderr, and a terminal session starts with all three pointing at /dev/pts/N. For an external command bash forks a child, applies the redirections in that child from left to right, then execs the program, which simply writes to fd 1 and 2 without knowing where they lead. > file is open(file, O_WRONLY|O_CREAT|O_TRUNC) followed by dup2(fd, 1); >> uses O_APPEND instead of O_TRUNC; 2>&1 is dup2(1, 2), a copy of where fd 1 points at that moment. That is why > file 2>&1 sends both to the file but 2>&1 > file sends errors to the old stdout. Truncation happens before the command runs, so sort f > f empties f. A pipe | connects one command's fd 1 to the next one's fd 0; stderr skips the pipe unless you use |&. Here-documents are fed to fd 0 through a pipe or temp file.

bash rewires the child's file descriptors, left to right, before the program starts.

type lineforkwriteserrorsYoucmd >log 2>&1bashyour shellChild processafter fork()fd 1 stdout-> terminalfd 2 stderr-> terminallog fileopened O_TRUNCTerminal/dev/pts/0
1/6
bash parses the line

bash splits off the redirections > log and 2>&1. They are not arguments: the program will never see them.

← → to step

Options you'll actually use

> filestdout to file, truncating it first
>> filestdout appended to file
2> filestderr to file (2>> appends)
> file 2>&1stdout and stderr both to file (order matters)
&> fileBash shorthand for > file 2>&1 (&>> appends)
2>/dev/nullThrow error messages away
< filestdin from a file
cmd1 |& cmd2Pipe stdout and stderr (same as 2>&1 |)
<<EOF ... EOFHere-document: lines as stdin, $vars expanded
<<'EOF'Quoted delimiter: no expansion, text is literal
<<-EOFStrip leading tabs (not spaces) from the lines
<<< "$var"Here-string: one string as stdin
set -o noclobber, >|Refuse to overwrite existing files with > / force it once
exec >> log 2>&1Redirect the rest of the script's output

Common mistakes

cmd 2>&1 > out.log

Redirections apply left to right: stderr copied the terminal before stdout moved, so errors are not in the log.

cmd > out.log 2>&1 # or: cmd &> out.log
sort data.txt > data.txt

The shell truncates data.txt before sort reads it, leaving an empty file. Same with sed, grep, awk.

sort -o data.txt data.txt # or write to a temp file, then mv
sudo echo 'line' >> /etc/hosts

The redirection is done by your unprivileged shell, not by sudo.

echo 'line' | sudo tee -a /etc/hosts > /dev/null
cmd > /dev/null to silence a noisy command

Only stdout is discarded; errors still print. Sometimes you want that, often you don't.

cmd > /dev/null 2>&1 # or keep errors: cmd > /dev/null
cat <<EOF > run.sh with $1 and $(date) in the body

An unquoted delimiter expands them while writing the file, baking in today's values.

cat <<'EOF' > run.sh

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Run ls on ~/app and on a missing path, sending the normal output to ok.txt and the errors to err.txt. Check both files.

  2. 2

    Capture both stdout and stderr of the same ls into one file all.txt, and count its lines.

  3. 3

    Create ~/app/motd.txt with three lines using a here-document, where one line shows your user name via $USER.

  4. 4

    Append the line '127.0.0.1 lab.local' to /etc/hosts using sudo correctly.

Related commands

teeCopy a stream to the screen and to files at oncebash: exit codesExit codes, $?, set -euo pipefail and trapcatPrint and concatenate filescrontabSchedule recurring jobs with cronsudoRun a command as root or another user
Previousbash: exit codesNextbash: arguments