newvar=$var[[ ... ]] constructWhen the shell performs parameter expansion, command substitution, variable or arithmetic expansion, it scans for word boundaries in the result. If any word boundary is found, then the result is split into multiple words at that position. The word boundary is defined by a shell variable IFS (Internal Field Separator). The default value for IFS are space, tab and newline, i.e. word splitting will occur on these three white space characters if not prevented explicitly.
set -x
var='I am
a
multiline string'
fun() {
echo "-$1-"
echo "*$2*"
echo ".$3."
}
fun $var
In the above example this is how the fun function is being executed:
fun I am a multiline string
$varis split into 5 args, onlyI,amandawill be printed.
To be more clear, let's create a script named showarg:
#!/usr/bin/env bash
printf "%d args:" $#
printf " <%s>" "$@"
echo
Now let's see the differences:
$ var="This is an example"
$ showarg $var
4 args: <This> <is> <an> <example>
$varis split into 4 args.IFSis white space characters and thus word splitting occurred in spaces
$ var="This/is/an/example"
$ showarg $var
1 args: <This/is/an/example>
In above word splitting didn't occur because the
IFScharacters weren't found.
Now let's set IFS=/
$ IFS=/
$ var="This/is/an/example"
$ showarg $var
4 args: <This> <is> <an> <example>
The
$varis splitting into 4 arguments not a single argument.
See what, when and why if you don't know about the affiliation of IFS to word splitting
let's set the IFS to space character only:
set -x
var='I am
a
multiline string'
IFS=' '
fun() {
echo "-$1-"
echo "*$2*"
echo ".$3."
}
fun $var
This time word splitting will only work on spaces. The fun function will be executed like this:
fun I 'am
a
multiline' string
$varis split into 3 args.I,am\na\nmultilineandstringwill be printed
Let's set the IFS to newline only:
IFS=$'\n'
...
Now the fun will be executed like:
fun 'I am' a 'multiline string'
$varis split into 3 args.I am,a,multiline stringwill be printed
Let's see what happens if we set IFS to nullstring:
IFS=
...
This time the fun will be executed like this:
fun 'I am
a
multiline string'
$varis not split i.e it remained a single arg.
You can prevent word splitting by setting the IFS to nullstring
A general way of preventing word splitting is to use double quote:
fun "$var"
will prevent word splitting in all the cases discussed above i.e the fun function will be executed with only one argument.
$ a='I am a string with spaces'
$ [ $a = $a ] || echo "didn't match"
bash: [: too many arguments
didn't match
[ $a = $a ]was interpreted as[ I am a string with spaces = I am a string with spaces ].[is thetestcommand for whichI am a string with spacesis not a single argument, rather it's 6 arguments!!
$ [ $a = something ] || echo "didn't match"
bash: [: too many arguments
didn't match
[ $a = something ]was interpreted as[ I am a string with spaces = something ]
$ [ $(grep . file) = 'something' ]
bash: [: too many arguments
The
grepcommand returns a multiline string with spaces, so you can just imagine how many arguments are there...:D
See what, when and why for the basics.
There are some cases where word splitting can be useful:
Filling up array:
arr=($(grep -o '[0-9]\+' file))
This will fill up
arrwith all numeric values found in file
Looping through space separated words:
words='foo bar baz'
for w in $words;do
echo "W: $w"
done
Output:
W: foo
W: bar
W: baz
Passing space separated parameters which don't contain white spaces:
packs='apache2 php php-mbstring php-mysql'
sudo apt-get install $packs
or
packs='
apache2
php
php-mbstring
php-mysql
'
sudo apt-get install $packs
This will install the packages. If you double quote the
$packsthen it will throw an error.
Unquoetd
$packsis sending all the space separated package names as arguments toapt-get, while quoting it will send the$packsstring as a single argument and thenapt-getwill try to install a package namedapache2 php php-mbstring php-mysql(for the first one) which obviously doesn't exist
See what, when and why for the basics.
We can just do simple replacement of separators from space to new line, as following example.
echo $sentence | tr " " "\n"
It'll split the value of the variable sentence and show it line by line respectively.
| Parameter | Details |
|---|---|
| IFS | Internal field separator |
| -x | Print commands and their arguments as they are executed (Shell option) |