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.