Reading input: read and here-docs

Bash · Practical · 12 min

Processing files line by line and embedding templates are bread-and-butter scripting tasks.

read reads one line from stdin into variables. Always use read -r (otherwise backslashes are eaten), and set IFS for the field separator: with IFS=: read -r a b c, the line is split at colons and the last variable gets the rest of the line.

The canonical loop for processing a file line by line is:

while IFS= read -r line; do ...; done < file

IFS= keeps leading and trailing whitespace, -r keeps backslashes, and the redirection after done feeds the whole loop. If the last line has no trailing newline, add || [[ -n $line ]] to the condition to still process it. Use _ as a throwaway variable for fields you skip.

Other useful flags: -p "prompt" shows a prompt, -s hides typing (passwords), -t 5 times out, -a arr reads words into an array.

A here-document passes inline text as stdin: cat <<EOF ... EOF. Variables and $( ) expand inside unless the delimiter is quoted (<<'EOF'), which makes it literal: ideal for generating config files or scripts. <<-EOF strips leading tabs so you can indent the block. A here-string <<< "$var" feeds a single string, handy for read and bc.

Example

cd "$(mktemp -d)"
printf 'alice 30\nbob 25\n' > people.txt
while read -r name age; do
  echo "$name is $age"
done < people.txt                 # alice is 30 / bob is 25
IFS=: read -r user _ uid _ < /etc/passwd
echo "$user has uid $uid"         # root has uid 0
read -r first rest <<< "one two three"
echo "$first | $rest"             # one | two three
who=World
cat <<EOF
Hello $who, today is $(date +%A)
EOF
cat <<'EOF'
No $expansion here
EOF
# read -rsp "Password: " pw       # interactive: hidden input

Remember

  • while IFS= read -r line; do ...; done < file is the line loop
  • The last variable of read collects the rest of the line
  • <<EOF expands variables, <<'EOF' is literal, <<< feeds one string

Watch out for

  • Piping into while read and expecting variables set in the loop to survive; the loop runs in a subshell.

Check yourself

  1. With `IFS=, read -r a b <<< "1,2,3"`, what is b?
  2. How do you stop $HOME from expanding inside a here-document?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.gnu.org/software/bash/manual/bash.html#Here-Documents