| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | bash 5.2.26-6.el10 |
| Applies to | Linux distributions with Bash installed |
| Privilege | Normal user; no root required for the examples |
| Scope | Bash IFS and word splitting: default space/tab/newline rules, whitespace vs non-whitespace delimiters, empty fields, unset vs empty IFS, temporary IFS on read, local IFS in functions, and how IFS affects "$*". Does not cover full string-to-array recipes, line-by-line file reading workflows, or the complete shell expansion order. |
| Related guides | Bash shell script tutorial Shell scripting course hub |
Bash IFS controls how unquoted expansion results are split into fields. Its default value contains space, tab, and newline, but you can temporarily change it to parse simple colon-, comma-, or other character-delimited input.
IFS does not split every string Bash sees. Splitting applies only to unquoted results of parameter expansion, command substitution, and arithmetic expansion. Literal text in your script was tokenized earlier by the shell parser. When unquoted expansion produces unexpected words, printf '<%s>\n' … makes each boundary visible.
What Is IFS?
IFS is a shell variable that holds the delimiter characters Bash consults during word splitting. The name Internal Field Separator reflects its role in dividing expansion results into fields.
By default, IFS is set to three whitespace characters:
- Space (
) - Tab (
$'\t') - Newline (
$'\n')
Printing "$IFS" directly may look blank because all three characters are whitespace. Use printf '%q\n' "$IFS" or declare -p IFS when you want to see its actual value:
declare -p IFSdeclare -- IFS=$' \t\n'If IFS is explicitly unset, Bash still performs word splitting as though the default space, tab, and newline value were in effect. GNU documents unset IFS and empty IFS as separate cases (see IFS Unset vs IFS Empty below).
Word splitting never runs on text inside single quotes, and double quotes suppress splitting on the quoted portion while still allowing $ expansions inside. That quoting behavior is separate from IFS, but it is the first thing to check when a value breaks apart unexpectedly.
When Bash Performs Word Splitting
Word splitting is one step in Bash's larger expansion pipeline. It runs on the results of parameter expansion, command substitution, and arithmetic expansion when those results are not protected by double quotes.
It does not run on:
- Words parsed directly from your script source
- Expansions inside double quotes (
"$var") - Simple assignments such as
name=value— see Bash variables for how assignment stores the expanded value without splitting it
For the surrounding steps (tilde expansion, globs, quote removal), see Bash single vs double quotes — that guide walks through where word splitting sits relative to quoting.
Watch splitting on a variable with internal spaces:
value='a b c'
printf '<%s>\n' $value<a>
<b>
<c>Double quotes keep the spaces intact because word splitting is suppressed on the expansion:
printf '<%s>\n' "$value"<a b c>The unquoted form produced three words. The quoted form stayed one word. That difference drives most real-world IFS bugs.
Default IFS: Space, Tab and Newline
With default IFS behavior, consecutive whitespace characters act as one delimiter, and leading or trailing whitespace around the split fields is ignored.
Try a value with extra spaces at the edges and between words:
unset IFS
wide=' a b '
printf '<%s>\n' $wide<a>
<b>Only a and b appear. The leading, trailing, and doubled internal spaces did not become empty fields. That collapsing behavior is specific to IFS whitespace characters.
IFS Whitespace Has Special Rules
Characters classified as IFS whitespace (space, tab, newline) follow different rules from other delimiter characters:
- A sequence of whitespace delimiters counts as one separator
- Leading and trailing whitespace around the fields is discarded in the usual splitting contexts
- You do not get empty fields from whitespace-only gaps the way you can with non-whitespace delimiters
Compare default whitespace splitting with a comma-only IFS on the same string shape:
unset IFS
line='a b,c'
printf '<%s>\n' $line<a>
<b,c>Space split a from b,c, but the comma stayed attached to b because comma is not a default delimiter.
Now restrict IFS to comma only:
IFS=,
line='a b,c'
printf '<%s>\n' $line<a b>
<c>Comma is the only separator now. Spaces inside a b are preserved because space is no longer in IFS.
Non-Whitespace IFS Delimiters
When IFS contains non-whitespace characters (colon, comma, pipe, and so on), each character is a delimiter on its own. Adjacent delimiters can produce empty fields between them. Do not assume a delimiter at the very end of a value will always produce a final empty argument; trailing empty fields depend on the splitting context.
Split a passwd-style line on colons:
line='a::c'
IFS=:
printf '<%s>\n' $line<a>
<>
<c>The empty angle brackets show a null field between the two colons. Non-whitespace delimiters preserve structure that whitespace delimiters would collapse away.
Split a comma-separated record with a missing middle column:
IFS=,
record='a,,c'
printf '<%s>\n' $record<a>
<>
<c>With ordinary unquoted word splitting, a single trailing delimiter does not necessarily add a final argument:
IFS=:
value='a:b:'
printf '<%s>\n' $value<a>
<b>Only a and b appear even though the value ends with :. That is why read -a can drop a trailing empty field in some setups while adjacent internal delimiters still yield empty fields.
When IFS contains both whitespace and non-whitespace delimiters, Bash applies special rules to the IFS whitespace adjacent to a non-whitespace delimiter.
With IFS=,, however, space is not a delimiter at all. A value such as a , , b is split only at the commas, so the surrounding spaces remain part of the resulting fields. IFS does not trim those spaces automatically.
IFS Unset vs IFS Empty
Unset IFS and IFS='' are not the same. Unset restores Bash's default splitting set (space, tab, newline). An empty IFS disables word splitting on unquoted expansions.
With IFS unset, words still split on whitespace:
unset IFS
value='one two'
printf '<%s>\n' $value<one>
<two>Set IFS to an empty string and the same value stays intact:
IFS=
value='one two'
printf '<%s>\n' $value<one two>An empty IFS disables word splitting on unquoted expansions. That behavior is useful to know when debugging, but it is not the usual fix for preserving spaces in a variable. Use IFS= read -r line when you want read to preserve leading and trailing whitespace. For ordinary variable expansions, quote the expansion ("$value") instead of changing IFS merely to prevent splitting.
Temporarily Set IFS for read
The Bash read command can split one input line into multiple variables when IFS contains delimiters. Prefixing IFS=… on the read command limits the change to that one invocation.
Parse a colon-separated passwd field from a here-string:
line='root:x:0:0:root:/root:/bin/bash'
IFS=: read -r user x uid gid gecos home shell <<< "$line"
printf 'user=%s uid=%s home=%s\n' "$user" "$uid" "$home"user=root uid=0 home=/rootSplit a simple comma-separated line into an indexed array with one prefixed IFS:
IFS=, read -ra fields <<< 'alpha,beta,gamma'
printf '<%s>\n' "${fields[@]}"<alpha>
<beta>
<gamma>-a fields stores the parsed fields in an indexed array. See Bash arrays for iterating and modifying elements after a split like this. Use the pattern for simple controlled delimiters; quoted CSV and more complex formats still need a real parser.
To read a whole line without trimming leading or trailing whitespace, set IFS empty for that read only:
printf ' spaced line \n' | { IFS= read -r line; printf '<%s>\n' "$line"; }< spaced line >IFS= on read keeps the line intact. Multi-line file loops, process substitution, and EOF edge cases belong in read a file line by line in Bash — this page focuses on how IFS shapes the fields read produces.
Use local IFS Inside Functions
Changing IFS in a function without local leaks into the caller's shell state. Prefer local IFS=… inside a Bash function so the delimiter choice does not persist.
This function splits its argument on commas and leaves global IFS alone:
show_fields() {
local IFS=,
printf '<%s>\n' $1
}
unset IFS
show_fields 'p,q,r'<p>
<q>
<r>After the function returns, default whitespace splitting still applies to the caller. Tab is still in default IFS, so store the sample in a variable and expand it unquoted:
sample=$'a\tb'
printf '<%s>\n' $sample<a>
<b>If show_fields had assigned IFS=, without local, the caller would still see comma splitting until IFS was restored manually.
How IFS Affects "$*" and Why "$@" Is Different
"$@" expands to one quoted word per positional parameter. Word splitting does not re-break those words, so "$@" preserves argument boundaries regardless of IFS.
"$*" is different. Bash joins all positional parameters into one string using the first character of IFS, then applies quoting rules.
Set IFS to comma and compare:
IFS=,
set -- alpha beta gamma
printf '<%s>\n' "$*"<alpha,beta,gamma>Each positional parameter stays separate with "$@":
printf '<%s>\n' "$@"<alpha>
<beta>
<gamma>Pass arguments to another command with "$@" when you need one argument per original word. Use "$*" only when you intentionally want a single joined string.
IFS Is Not a Multi-Character Separator
IFS is a character set, not a multi-character delimiter string. Setting IFS='::' does not mean “split on the two-character sequence ::.” It means both : characters are individual delimiters.
Demonstrate on a::b:
IFS='::'
text='a::b'
printf '<%s>\n' $text<a>
<>
<b>Bash split on each colon separately, leaving an empty field between them. That matches IFS=: on the same string. There is no built-in IFS syntax for “treat :: as one separator.”
Why IFS Is Not a CSV Parser
Comma-separated text with quoted fields, embedded commas, and escape rules cannot be parsed reliably with IFS alone.
Naive comma splitting mangles a CSV-shaped line:
csv='a,"b,c",d'
IFS=,
printf '<%s>\n' $csv<a>
<"b>
<c">
<d>The quote characters stored inside csv do not give the embedded comma any special meaning to Bash. IFS sees the comma as another delimiter because it does not implement CSV quoting rules. Use a proper CSV tool, a dedicated parser, or a language library when the input is true CSV. For turning a simple delimited string into a Bash array when the format is controlled, see split a string into an array in Bash.
Debug Word Boundaries with printf
When a script mis-parses input, print each word wrapped in angle brackets before you change IFS or quoting:
printf '<%s>\n' $valueEach line shows one post-splitting word. Pair that with Bash echo vs printf when you also need to inspect escape sequences or format fixed columns.
Compare the same variable quoted and unquoted when debugging. If you changed IFS earlier in the same shell (for example IFS=, in the CSV example above), reset default splitting first or the unquoted line will not split on spaces:
unset IFS
value='a b c'
printf 'unquoted: '; printf '<%s>\n' $value
printf 'quoted: '; printf '<%s>\n' "$value"unquoted: <a>
<b>
<c>
quoted: <a b c>With IFS still set to comma, the same unquoted command would print one field (<a b c>) because space is no longer a delimiter. The bracket lines make off-by-one field bugs obvious once IFS matches what you intend.
Common IFS Bugs
| Symptom | Likely cause | Fix |
|---|---|---|
| Script splits differently after calling a helper | Global IFS changed inside a function |
Use local IFS=… or restore IFS before return |
| Words break on spaces unexpectedly | Unquoted $variable expansion |
Quote expansions: "$variable" |
read trims leading/trailing spaces |
Default IFS whitespace rules |
Use IFS= read -r line for a raw line |
Expected :: as one delimiter |
IFS is a character set, not a string |
Split with a different tool or parse in a loop |
| Empty columns vanish with spaces | Used whitespace rules where you needed , or : |
Set IFS to the non-whitespace delimiter you actually need |
| CSV rows corrupt | Used IFS as a CSV parser |
Use a CSV-aware parser, not bare word splitting |
Reproduce global IFS leakage by assigning IFS=, in a function without local. The caller keeps comma splitting until you unset or reset IFS.
Summary
Bash IFS controls word splitting on unquoted expansion results, not every string literal in your source. Default IFS whitespace collapses runs of spaces, tabs, and newlines; non-whitespace delimiters such as : and , can preserve empty fields between adjacent separators.
Unset IFS restores default whitespace splitting. IFS='' disables splitting entirely until you change it back. Quote ordinary expansions with "$value" when you need to preserve spaces; prefix IFS=… on a single read command or declare local IFS=… inside functions when you need a temporary delimiter without polluting global shell state.
"$@" keeps one argument per positional parameter. "$*" joins parameters with the first character of IFS into a single string. Treat IFS as a set of delimiter characters, not a multi-character separator string, and do not use it as a CSV parser when fields can contain quotes or embedded commas.
For controlled string-to-array conversion and full line-reading loops, use the dedicated guides linked above rather than stretching bare IFS splitting beyond what it safely parses.
References
- Bash manual — Word Splitting —
IFSwhitespace rules and empty fields - Bash manual — Special Parameters —
"$*"joining withIFS - Bash manual — Bourne Shell Builtins — read —
readandIFS
Frequently Asked Questions
What is the default IFS?
The default Internal Field Separator is space, tab, and newline. declare -p IFS normally shows declare -- IFS=$' \t\n'. Printing "$IFS" alone may look blank because all three characters are whitespace. When IFS is unset, Bash still splits on that default set.
What is the difference between unset IFS and IFS=''?
Unset IFS keeps normal whitespace splitting. Setting IFS to an empty string disables word splitting on unquoted expansions until IFS is changed again.
Why use IFS= read -r?
IFS= on a read command prevents leading and trailing whitespace from being stripped as field separators and keeps the line intact. -r prevents backslash escaping of the input.
Does IFS affect "$@"?
No. "$@" expands each positional parameter as a separate quoted word regardless of IFS. Word splitting does not re-split those words.
Can IFS split on :: as one delimiter?
No. IFS lists individual delimiter characters. IFS='::' treats each colon separately, not the two-character sequence as a unit.
Can I use IFS for CSV?
Not reliably. CSV allows quoted fields, embedded commas, and other rules that IFS word splitting does not implement. Use a CSV-aware parser instead.

