Reliable scripts are not only about what commands run. They are also about where output goes, what gets logged, and how downstream commands receive input.
Table of Contents
This post focuses on output routing patterns you will use in almost every script.
Output Streams
Combining and Controlling Streams
Pipelines and Logging
Series Navigation
Output streams
stdout and stderr in practice
Most commands write normal output to stdout (file descriptor 1) and errors to stderr (file descriptor 2).
echo "normal message"
ls /does-not-existOverwrite versus append redirects
Use > to overwrite and >> to append.
echo "first line" > output.log
echo "second line" >> output.logCombining and controlling streams
Redirect stderr only
You can silence only errors while leaving normal output visible.
ls /etc /does-not-exist 2>/dev/nullMerge stdout and stderr
Route both streams to one place when troubleshooting or collecting logs.
./run-job.sh > job.log 2>&1
./run-job.sh &> job.log
./run-job.sh &>> job.log2>&1 means “send stderr to wherever stdout currently goes”.
Avoid accidental overwrite
If you want basic protection against clobbering files, enable noclobber.
set -o noclobber
echo "safe write" > existing.logWhen you intentionally need overwrite semantics, disable it for that script section.
Pipelines and logging
Pipe command output
Pipes send stdout from one command into stdin of the next command.
ps aux | grep nginx | wc -lUse tee for live output plus log files
tee lets you keep output in the terminal while writing the same data to a file.
./deploy.sh 2>&1 | tee deploy.logAppend mode preserves previous logs.
./deploy.sh 2>&1 | tee -a deploy.logCapture success and failure cleanly
A small pattern that keeps output organized:
#!/usr/bin/env bash
run_report() {
local report_file="$1"
if ./build-report.sh >"$report_file" 2>error.log; then
echo "report generation succeeded"
return 0
fi
echo "report generation failed, see error.log"
return 1
}Next in this series
Next, we will use stdin and read, then move into positional arguments so scripts can accept interactive input and command-line parameters.