Chapter 03Lesson 02~55 minutes

Output Redirection, Append, noclobber, and tee

Output redirection is deceptively compact syntax. A single character can overwrite a file, merge diagnostics into normal output, or change which destination a descriptor references. This lesson treats redirection as production I/O wiring rather than shorthand.

BeginnerStreams & pipelinesHands-on lab

Learning objectives

By the end of this lesson

  • Use overwrite and append redirection deliberately.
  • Redirect stdout and stderr independently or together.
  • Explain why redirection order changes behavior.
  • Use noclobber as a guardrail against accidental overwrite.
  • Apply tee and grouped redirection to create auditable automation logs.

1. > replaces; >> appends

The most common output redirections connect stdout to a regular file. > opens the destination for writing and normally truncates an existing file. >> opens it for append.

printf 'first\n' > report.txt
printf 'second\n' > report.txt
cat report.txt

printf 'third\n' >> report.txt
cat report.txt
Destructive operator

Treat > as a state-changing operation. If the target path is wrong, existing content can be lost before the command itself does useful work.

2. Prefix the redirection with the descriptor you mean

When no descriptor is written, output redirection defaults to stdout, descriptor 1. To redirect stderr, use descriptor 2 explicitly.

command_that_might_fail() {
  printf 'normal record\n'
  printf 'warning record\n' >&2
}

command_that_might_fail >stdout.log 2>stderr.log

printf '%s\n' '--- stdout.log ---'
cat stdout.log
printf '%s\n' '--- stderr.log ---'
cat stderr.log
SyntaxEffectNote
>fileRedirect stdoutEquivalent to 1>file
2>fileRedirect stderrKeeps stdout unchanged
>>fileAppend stdoutDoes not truncate existing content
2>>fileAppend stderrUseful for diagnostic logs

3. Merge stderr into the current stdout destination

The form 2>&1 duplicates the destination currently used by stdout onto descriptor 2.

{
  printf 'normal\n'
  printf 'diagnostic\n' >&2
} >combined.log 2>&1

cat combined.log

Bash also supports &>combined.log as a convenient Bash-specific shorthand for redirecting both stdout and stderr to one file. The explicit form is worth learning because it exposes descriptor semantics and is more portable across shell dialects.

4. Redirections are processed from left to right

These two commands are not equivalent:

# Both streams end up in all.log:
some_command >all.log 2>&1

# stderr is duplicated to the shell's OLD stdout first;
# only stdout is then redirected to output.log:
some_command 2>&1 >output.log
Why order matters
flowchart TB
  A["Start: stdout -> terminal; stderr -> terminal"] --> B[" >all.log"]
  B --> C["stdout -> all.log; stderr -> terminal"]
  C --> D[" 2>&1"]
  D --> E["stdout -> all.log; stderr -> all.log"]
Read redirection left to right

2>&1 means “make stderr point wherever stdout points right now,” not “permanently link stderr to whatever stdout may become later.”

5. noclobber can block accidental regular-file truncation

Bash's noclobber option makes a normal > redirection fail when the destination is an existing regular file. It is a useful interactive and scripting guardrail, though it does not replace correct path validation.

set -o noclobber

printf 'protected\n' > protected.txt

# This second write fails instead of truncating the file:
printf 'replacement\n' > protected.txt ||   printf 'overwrite blocked\n' >&2

cat protected.txt

set +o noclobber

When you intentionally need to override noclobber, Bash provides >|. Use that operator only when the overwrite is explicitly part of the design.

set -o noclobber
printf 'forced replacement\n' >| protected.txt
set +o noclobber
cat protected.txt

6. tee copies stdin to files and stdout

tee is useful when you want to preserve a stream in a file while still allowing it to continue through the terminal or pipeline.

printf '%s\n' alpha beta gamma | tee values.log

printf '%s\n' delta | tee -a values.log

cat values.log
tee duplicates a stream
flowchart LR
  C["command stdout"] --> T["tee"]
  T --> F["log file"]
  T --> O["tee stdout -> terminal or next pipe"]

Remember that tee receives only the stream connected to its stdin. stderr from the command on the left does not automatically enter the pipe.

7. Include diagnostics in tee only when that is your intention

To send both streams through tee, merge stderr into stdout before the pipe:

{
  printf 'result\n'
  printf 'warning\n' >&2
} 2>&1 | tee combined.log

This is useful for a human-oriented session log. It is usually a bad choice when stdout is a machine-readable API, because merging destroys the distinction between result data and diagnostics.

8. Redirect a whole command group once

Brace groups let several commands share one redirection without opening the file repeatedly.

{
  printf '=== environment ===\n'
  printf 'user=%s\n' "$(whoami)"
  printf 'pwd=%s\n' "$PWD"
  printf 'bash=%s\n' "$BASH_VERSION"
} > environment.txt

cat environment.txt

The closing brace must be followed by a command terminator such as a newline or semicolon. Brace groups run in the current shell context, unlike parenthesized subshell groups.

9. Build logs without corrupting command results

A common production pattern is to keep stdout reserved for a result and write diagnostics to a separate log file. Allocate a descriptor for logging:

log_file="build.log"
exec 3>>"$log_file"

printf 'build started\n' >&3
artifact="/tmp/app.tar.gz"
printf 'artifact=%s\n' "$artifact" >&3

# Clean machine-readable output:
printf '%s\n' "$artifact"

exec 3>&-

This pattern becomes more useful later when functions, timestamps, log levels, and traps are introduced.

10. Redirection does not make multi-command updates atomic

Writing through > can truncate a file before all content is successfully generated. For important configuration files, a safer pattern is often: write a temporary file, validate it, then rename it into place.

target="generated.conf"
tmp=$(mktemp "${target}.XXXXXX")

if generate_configuration >"$tmp"; then
  # validate_configuration "$tmp" would go here
  mv -- "$tmp" "$target"
else
  rm -f -- "$tmp"
  exit 1
fi
Reliability principle

Redirection controls where bytes go; it does not by itself provide transactional file updates.

11. Hands-on lab: create separate result and session logs

Create a script whose stdout is a single artifact path while stderr is copied both to the terminal and to a persistent log.

mkdir -p "$HOME/devops-academy/bash/chapter03/lesson02"
cd "$HOME/devops-academy/bash/chapter03/lesson02"

cat > build.sh <<'EOF'
#!/usr/bin/env bash

artifact="./output/app.tar.gz"
mkdir -p ./output

printf 'preparing build directory\n' >&2
printf 'fake artifact content\n' > "$artifact"
printf 'build completed\n' >&2

printf '%s\n' "$artifact"
EOF

# Capture stdout as the result.
# Send stderr through tee, but route tee's own stdout back to stderr.
artifact=$(
  bash build.sh 2> >(tee -a build.log >&2)
)

printf 'caller received artifact: %s\n' "$artifact"
printf '%s\n' '--- persistent log ---'
cat build.log

Verification checklist

12. Knowledge check

Question 1. What is the key difference between > and >>?

Question 2. Why does cmd >file 2>&1 differ from cmd 2>&1 >file?

Question 3. What does tee do?

13. Summary

Output redirection is descriptor wiring with real state-changing consequences. Use > only when replacement is intended, >> for append, descriptor numbers to preserve stream separation, and tee when one stream must continue while also being recorded. Redirection order is semantic, not stylistic.

14. Further reading

  • GNU Bash Reference Manual — Redirections and the noclobber option.
  • GNU Coreutils manual — tee.
  • POSIX Shell Command Language — redirection ordering.
  • Linux dup(2) manual page — descriptor duplication semantics.
Next lesson

Input Redirection, Here Documents, and Here Strings

The next lesson continues the Chapter 3 stream and redirection model.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.