Skip to content
Linux Basics: Read Text with Bash read and mapfile

Linux Basics: Read Text with Bash read and mapfile

October 19, 2026

Preserve spaces, split a simple record, or load a short list into an array. Choose the input method that matches your text format.

These examples were tested on Linux with GNU Bash 5.2.37. Use Bash rather than sh. No packages or administrator access are needed.

Prepare a scratch directory

You need Bash, a text editor, and the mktemp, rm, and rmdir utilities. Keep using the same terminal. Save each named source file in this scratch directory before running its command. Calling bash explicitly avoids changing executable permissions.

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

1. Preserve lines and a missing final newline

Save this as read-lines.sh:

#!/usr/bin/env bash
count=0
while IFS= read -r line || [[ -n "$line" ]]; do
  ((count += 1))
  printf '%d: <%s>\n' "$count" "$line"
done < <(printf '%s\n' '  keep spaces  ' 'C:\tmp'; printf '%s' 'last line')
printf 'Lines: %d\n' "$count"

Run it:

bash read-lines.sh

Output:

1: <  keep spaces  >
2: <C:\tmp>
3: <last line>
Lines: 3

Empty IFS keeps surrounding whitespace; -r preserves backslashes. The extra test processes a final nonempty line when EOF arrives without a newline. Process substitution feeds the loop without running the loop in a pipeline subshell.

2. Split a controlled delimiter format

Save this as read-fields.sh:

#!/usr/bin/env bash
IFS=: read -r service port < <(printf '%s\n' 'preview:8080')
printf 'Service: %s\nPort: %s\n' "$service" "$port"

Run it:

bash read-fields.sh

Output:

Service: preview
Port: 8080

Here IFS=: applies to this read. The last variable receives the remaining fields, so this is appropriate for a controlled two-field format. It is not a CSV parser and does not understand quoted separators.

3. Load a small list into an array

Save this as mapfile-list.sh:

#!/usr/bin/env bash
mapfile -t tasks < <(printf '%s\n' 'review draft' 'run tests' 'publish preview')
printf 'Tasks: %d\n' "${#tasks[@]}"
for index in "${!tasks[@]}"; do
  printf '%d. %s\n' "$((index + 1))" "${tasks[index]}"
done

Run it:

bash mapfile-list.sh

Output:

Tasks: 3
1. review draft
2. run tests
3. publish preview

The -t flag removes each terminating newline. Quoted array expansions preserve spaces in task names. mapfile requires Bash 4 or newer and stores the list in memory; use a read loop for a large stream.

Clean up

In the same terminal, remove only the example files, return to your original directory, and remove the empty scratch directory:

rm -- read-lines.sh read-fields.sh mapfile-list.sh
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

Reference

See the GNU Bash manual: Bash Builtins for the relevant shell behavior. The installed Bash help command also documents these builtins.

Acknowledgements

This post was created with assistance from AI.

Last updated on