Split a String into an Array in Bash

Tested on RHEL 10.2 (Coughlan)
Package bash-5.2.26-6.el10.x86_64
Applies to Linux distributions with Bash installed
Privilege Normal user
Scope Splitting one string into a Bash indexed array with IFS and read -ra: whitespace and single-character delimiters, empty fields, unsafe arr=($string), multi-character delimiter limits, and a CSV counterexample. Does not cover array CRUD, full IFS theory, or mapfile for files and multiline streams.
Related guides Bash shell script tutorial
Shell scripting course hub
Short on time?Beta Get the gist of this 10-minute read in seconds, then ask follow-up questions

You split a string into a Bash array when one variable holds several values separated by spaces, commas, colons, or another delimiter. The usual pattern is IFS=... read -ra array <<< "$string", which fills indexed array elements without pathname expansion.

IFS names a set of delimiter characters, not a multi-character token. That distinction matters for version numbers, PATH-style lists, and any input where commas sit inside quoted CSV fields. This guide focuses on splitting one string into an indexed array. For array iteration, slicing, and modification, see the Bash arrays guide.

Need Pattern
Whitespace fields read -ra arr <<< "$string"
One delimiter IFS=',' read -ra arr <<< "$string"
Several delimiter chars `IFS=',;
Preserve spaces in fields choose a delimiter that does not appear inside values (often |)
Multiline or file input use mapfile / line-reading patterns — not this article

Quick Answer: Split a String into a Bash Array

For most single-line strings, assign the delimiter to IFS and read into an array with -a:

bash
string="red,green,blue"
IFS=',' read -ra colors <<< "$string"
printf '%s\n' "${colors[@]}"
output
red
green
blue

read -ra reads one input record (here from a here-string), splits it using IFS, and stores the fields in colors. This is also the usual way to convert a delimited Bash string to an indexed array. The -r flag prevents backslashes in the input from being treated as escape characters, so read -ra is the safe default when splitting arbitrary strings. Use "${colors[@]}" when passing elements to another command so spaces inside a field stay intact.


Split on Whitespace

When fields are separated by spaces or tabs, read uses the default IFS whitespace rules:

bash
string="apple banana orange"
read -ra fruits <<< "$string"
printf '%s\n' "${fruits[@]}"
output
apple
banana
orange

This matches a simple word list. It is not the right tool when a field itself contains spaces unless you switch to a non-whitespace delimiter.


Split on a Single Delimiter

Set IFS to one character before read. The assignment applies only to that read call when written on the same line:

bash
string="red,green,blue"
IFS=',' read -ra colors <<< "$string"
printf '%s\n' "${colors[@]}"
output
red
green
blue

The Bash read builtin accepts -a to fill an array from one line of input. Always pair it with -r when the string may contain backslashes:

bash
string='C:\Temp\file,backup'
IFS=, read -a arr <<< "$string"
printf '%s\n' "${arr[@]}"
output
C:Tempfile
backup

Without -r, read treats backslash as an escape and alters the field text — the first element is C:Tempfile, not C:\Temp\file. read -ra preserves backslashes:

bash
IFS=, read -ra arr <<< "$string"
printf '%s\n' "${arr[@]}"
output
C:\Temp\file
backup

Split Comma-, Colon- and Pipe-Separated Values

The same pattern works for other single-character separators.

Colon-separated paths behave like PATH entries:

bash
path="/usr/local/bin:/usr/bin:/bin"
IFS=: read -ra dirs <<< "$path"
printf '%s\n' "${dirs[@]}"
output
/usr/local/bin
/usr/bin
/bin

Version strings split on dots:

bash
ver="1.2.3"
IFS=. read -ra parts <<< "$ver"
printf 'major=%s minor=%s patch=%s\n' "${parts[0]}" "${parts[1]}" "${parts[2]}"
output
major=1 minor=2 patch=3

Pipe-separated records can carry spaces inside a field:

bash
record="nginx|active|/var/log/nginx/access.log"
IFS='|' read -ra fields <<< "$record"
printf 'service=%s status=%s log=%s\n' "${fields[0]}" "${fields[1]}" "${fields[2]}"
output
service=nginx status=active log=/var/log/nginx/access.log

If the record must contain exactly three fields, check [[ ${#fields[@]} -eq 3 ]] before using the values.


What IFS Really Means

IFS is a set of delimiter characters, not a multi-character separator string. Each character in IFS can end a field. This section covers what you need for one-line string-to-array splits; for word splitting on unquoted expansions, empty IFS, and local IFS in functions, see Bash IFS.

Bash treats IFS whitespace differently from delimiters such as , or :. Runs of spaces, tabs, and newlines are normally collapsed during field splitting, while adjacent non-whitespace IFS characters can produce empty fields:

bash
string=$(printf 'a\x20\x20\x20b')
read -ra arr <<< "$string"
printf 'count=%s\n' "${#arr[@]}"
printf '%s' "$string" | od -An -tx1
output
count=2
 61 20 20 20 62

The printf line builds a, three ordinary ASCII spaces (0x20), and b — equivalent to string='a b' with three normal spaces. Those spaces collapse to one delimiter, so you get two elements. That is why a,,,b behaves differently.

Setting IFS='::' does not mean “split only on the two-character sequence ::.” Bash still treats : as the delimiter:

bash
text="a::b::c"
IFS="::" read -ra arr <<< "$text"
printf 'count=%s\n' "${#arr[@]}"
printf '<%s>\n' "${arr[@]}"
output
count=5
<a>
<>
<b>
<>
<c>

Each <> line is an empty array element. You get empty fields between colons, not clean a, b, c tokens. For a true multi-character separator, transform the string first or use a purpose-built tool — see Multi-Character Delimiters below.

You can list several delimiter characters at once:

bash
string="apple,banana;orange|grape"
IFS=',;|' read -ra arr <<< "$string"
printf '%s\n' "${arr[@]}"
output
apple
banana
orange
grape

Any comma, semicolon, or pipe ends a field.


Preserve Spaces Inside Fields

Whitespace splitting breaks when a value contains spaces. Pick a delimiter that does not appear inside the data — often | for simple service records:

bash
string="apple|red banana|orange"
IFS='|' read -ra arr <<< "$string"
printf '%s\n' "${arr[@]}"
output
apple
red banana
orange

The middle element keeps its internal space because IFS was | only, not space.


Empty Fields and Adjacent Delimiters

With a non-whitespace delimiter such as comma, adjacent separators can yield empty array elements:

bash
text="a,,b,c"
IFS=, read -ra arr <<< "$text"
printf 'count=%s\n' "${#arr[@]}"
printf '[%s]\n' "${arr[@]}"
output
count=4
[a]
[]
[b]
[c]

The empty field between the two commas becomes arr[1]="". Whether you want to keep or drop empty fields depends on your input format — filter them in a for loop if the protocol does not use empty slots.

read -a does not preserve every trailing empty field. A string that ends with the delimiter drops the final empty element:

bash
text='a,b,'
IFS=, read -ra arr <<< "$text"
declare -p arr
output
declare -a arr=([0]="a" [1]="b")

You get a and b, not a third empty element after the trailing comma. Adjacent non-whitespace delimiters can create empty fields in the middle of the array, but read -a is not lossless for trailing empties. If trailing empty fields carry meaning, use a parser designed for that format or handle the trailing delimiter explicitly.


Why arr=($string) Is Unsafe

Unquoted arr=($string) runs word splitting and pathname expansion. A literal * in the string can expand to filenames in the current directory. Create a throwaway directory with a few files so the glob has something to match:

bash
tmpdir=$(mktemp -d) && cd "$tmpdir" && touch a.txt b.txt

With a.txt and b.txt in the working directory, run the unsafe form:

bash
string="keep * end"
arr=($string)
printf 'unsafe: %s\n' "${arr[@]}"
output
unsafe: keep
unsafe: a.txt
unsafe: b.txt
unsafe: end

The * was meant as text, but Bash replaced it with matching files. read -ra does not glob:

bash
read -ra arr <<< "$string"
printf 'safe: %s\n' "${arr[@]}"
output
safe: keep
safe: *
safe: end

Avoid arr=($string) for general string splitting. It performs both word splitting and pathname expansion; use IFS=... read -ra when you want delimiter-based splitting into an array. The form can be intentional in tightly controlled shell code, but it is a poor default for parsing data.


Multi-Character Delimiters

Do not simulate a multi-character delimiter with IFS alone. A common workaround for a fixed token such as :: is to replace it with a single-character separator before read:

bash
text="host::port::db"
IFS=: read -ra tmp <<< "${text//::/:}"
printf '%s\n' "${tmp[@]}"
output
host
port
db

This works only when the replacement character cannot occur independently in the data. Otherwise the replacement makes original delimiters and existing characters indistinguishable. For arbitrary multi-character delimiters, use a parser that explicitly matches the full delimiter instead of relying on IFS.


Why IFS Is Not a CSV Parser

Comma splitting is not CSV parsing. Quoted fields contain commas that must stay inside one value:

bash
csv='"Smith, John",42'
IFS=, read -ra arr <<< "$csv"
printf '%s\n' "${arr[@]}"
output
"Smith
 John"
42

IFS=, saw the comma inside the name and split there. For real CSV with quoting and escaping, use a CSV-aware library or tool — not IFS alone.


Split One Line vs Read Multiple Lines

read -ra handles one string / one line of input. When the source is a multiline variable, a here-document, or a file with one record per line, use line-oriented tools instead:

This article stops at turning one delimited string into array elements. Building, looping, and deleting elements belong in the Bash arrays guide near the start of this page.


Common Mistakes

Symptom Likely cause Fix
Extra filenames in the array arr=($string) globbed * or ? Use read -ra or quote expansions
a::b splits into many empty pieces IFS treats each : separately Replace :: or use a real multi-char parser
Field with spaces breaks apart Default whitespace IFS Use |, comma, or another delimiter
CSV name column splits on comma IFS=, ignores quotes Use a CSV parser, not IFS
Later commands split oddly IFS left changed globally Keep IFS=... on the same line as read
Trailing comma loses final empty field read -a drops trailing empties Handle the trailing delimiter explicitly or use another parser
Backslashes disappear after split read -a without -r Use read -ra
Need one element per file line Used read -ra on multiline text Use mapfile or a while read loop

Summary

Splitting one Bash string into an array — converting a delimited string to an indexed array — usually means IFS=delimiter read -ra array <<< "$string". Use -ra so backslashes are not eaten during the read. Whitespace, comma, colon, pipe, and dot each work when IFS is set to that single character (or to a set of characters). read fills the array for one input record; expand with "${array[@]}" when passing elements to other commands.

Remember that IFS lists delimiter characters, not a multi-character separator, and that whitespace IFS collapses runs while non-whitespace delimiters can leave empty middle fields — but not trailing empty fields after read -a. Avoid arr=($string) for parsing; it word-splits and glob-expands. Quoted CSV needs a real parser, not IFS=,.

For multiline input or entire files, switch to line-reading tools instead of stretching this pattern. Once the string is split, loop, slice, and modify the elements with normal array syntax.


References


Frequently Asked Questions

How do I split a string by comma in Bash?

Set IFS=',' and use read -ra: IFS=',' read -ra arr <<< "$string". Each comma-separated segment becomes one array element.

Can IFS use a multi-character delimiter?

No. IFS is a set of single delimiter characters. IFS='::' still splits on each colon individually. Replace the token in the string or use another tool for true multi-character separators.

Why did * in my string turn into filenames?

Unquoted arr=($string) performs pathname expansion. A * in the string matches files in the current directory. Use read -ra instead.

Can I split CSV with IFS=,?

Not reliably. Commas inside quoted fields are still delimiters to IFS. Use a CSV-aware parser when quoting and escaping matter.

When should I use mapfile instead of read -ra?

Use mapfile (or readarray) when each line of a file or multiline string should become one array element. Use read -ra when one delimited string on a single line should become multiple elements.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)