Quoting to split into an array

Assigning `arr=( $var )` re-splits on IFS and globs. Use `read -ra` or `mapfile` for safe splitting.

Problem

`arr=( $string )` splits on whitespace AND expands globs in the value. For a string like `a *.txt c`, the array silently fills with every `.txt` file in the current directory. For a string with no spaces but containing `?`, the same surprise applies.

Bad

csv="alpha,beta,gamma"
IFS=',' arr=( $csv )   # also globs each element

Good

csv="alpha,beta,gamma"
IFS=',' read -ra arr <<< "$csv"

# To read lines of a file:
mapfile -t lines < file.txt

Explanation

`read -ra` splits on `IFS` without globbing. `mapfile` (a.k.a. `readarray`) reads line-by-line and is the canonical way to load a file into an array. Neither performs pathname expansion on the values.

Related

SC2068

SC2128

When It Matters

arr=($var) splits the variable on IFS and glob-expands each piece, so a value containing a space, an asterisk, or a question mark produces an array that does not match the input. Scripts that build arrays from configuration values, environment variables, or command output hit this whenever real-world data arrives. The result is not an error, just a wrong number of elements — which surfaces much later as an argument list that is off by one.

Second Example

Note

read -a performs the split without pathname expansion, so an element containing * stays an asterisk instead of becoming a directory listing.

Exceptions

When you control the value and know it contains only whitespace-free tokens, the split form is fine — and with set -f active, globbing is off and the behaviour is fully predictable. Document that assumption where the array is built.

Faq

Q

How do I split on a character other than whitespace?

A

Set IFS for the single command: IFS=, read -r -a arr <<< "$csv". The assignment prefix applies only to that command.

Q

Why does read -a lose the last field sometimes?

A

A here-string appends a newline, so trailing empty fields behave differently than expected. For data with meaningful empty trailing fields, append a sentinel or use mapfile with a delimiter.

Q

Is there a difference between ${arr[@]} and ${arr[*]}?

A

Quoted, [@] expands to one word per element and [*] joins everything into a single word using the first character of IFS.