Skip to content

Chapter 7 of 8

Input/Output, Redirection, and File Handling

The read builtin is the primary way to ingest input. read -rp "Prompt: " answer reads a single line with a prompt and without backslash interpretation, while while IFS= read -r line; do ...; done < file.txt is the canonical pattern for iterating over a file. The variant read -d DELIM changes the line terminator, which is essential for NUL-delimited input via read -d '' in loops paired with find -print0. To count lines in a file portably, wc -l < file.txt avoids the filename in the output, and grep -c '' file is an alternative that tallies newlines.

Redirection steers streams between commands and files. Stdout is redirected with > (truncate) or >> (append), and stderr with 2> or 2>>. To send stderr to wherever stdout currently points, write 2>&1 — order matters because redirections are processed left to right. To silence both streams, redirect to /dev/null: cmd > /dev/null 2>&1. To capture both into a single variable, out=$(cmd 2>&1) works; to keep them separate, write to temp files: cmd >out 2>err. The exec builtin, when given a redirection but no command, modifies the current shell's file descriptors — exec > file makes all subsequent echo and printf output go to that file until further notice, and is a handy way to redirect a long script's output to a log without sprinkling redirections everywhere.

For multi-line text, Bash provides here-documents and here-strings. A here-document is written cat <<EOF followed by lines and a closing EOF; quoting the delimiter (<<'EOF') disables parameter expansion inside, while an unquoted delimiter allows \(USER and other expansions to be substituted. Here-strings are the single-line equivalent: command <<< "\)variable" feeds the variable's value to the command's standard input without spawning a subshell. To produce filenames safely, use tmp=\((mktemp) for a unique temporary file or tmpd=\)(mktemp -d) for a directory; these avoid the race conditions and predictable-path pitfalls of hand-rolled names in /tmp. To extract the directory and filename parts of a path, basename /a/b/c.sh gives c.sh and dirname /a/b/c.sh gives /a/b. A robust pattern for finding a script's own directory is dir=$(cd "$(dirname "\({BASH_SOURCE[0]}")" && pwd), which prefers \){BASH_SOURCE[0]} over $0 because the latter may be just the program name rather than a real path.

All chapters
  1. 1Foundations of Bash Scripting
  2. 2Variables, Expansion, Quoting, and Strict Mode
  3. 3Conditionals and Tests
  4. 4Loops and Iteration
  5. 5Functions, Sourcing, and Command-Line Arguments
  6. 6Arrays, Strings, Substitutions, and Arithmetic
  7. 7Input/Output, Redirection, and File Handling
  8. 8Process Control, Scheduling, and Robust Practices

Drill it

Reading is not remembering. These come from the Bash Scripting For Automation deck:

Q

What is Bash?

Bash (Bourne Again SHell) is a Unix shell and command language written as a free software replacement for the Bourne shell (sh). It is the default login shell o...

Q

What file extension is conventionally used for Bash scripts?

.sh. While not strictly required for execution, .sh signals intent and lets tooling treat the file as a shell script.

Q

What must the first line of an executable Bash script be?

A shebang line, e.g. #!/usr/bin/env bash or #!/bin/bash, which tells the kernel which interpreter to invoke.

Q

What command makes a script file executable?

chmod +x script.sh (or chmod 755 script.sh).