Learn › Linux Foundations › Bash Shell Environment
bash - a hands-on Linux lab on a real virtual machine.
After the shell expands an unquoted variable or command substitution, it splits the result into separate words on IFS (space, tab, newline by default). That is word splitting, and it is the number-one reason a beginner's command quietly does the wrong thing. Watch one variable become three loop words, watch a spaced filename split into two files with touch $name, then fix it with double quotes: "$var" turns splitting off and keeps the value as one word. Quote by default and the surprises stop.
You type one command. You name one file. The shell makes two. Nothing errored, nothing warned, and now there is a stray file sitting in your folder that you never asked for.
This is not a bug, and it is not bad luck. It is one specific rule the shell follows every time it expands a variable, and once you have seen it happen you will never be caught by it again. The rule has a name: word splitting. It is the single most common reason a beginner's command quietly does the wrong thing, and by the end of this lesson you will control it with one keystroke.
The black boxes in this lesson are a practice terminal: a safe sandbox that only checks the one command each step teaches, so you cannot break anything. Everything you see printed here is real, exact shell output. Word splitting is a fixed rule, so these results are the same on every machine. Type along and watch it happen.
When you write a variable like $files, the shell first replaces it with the value stored inside. That step is called expansion. But the shell is not finished. If that variable was NOT wrapped in double quotes, the shell takes the expanded value and chops it into separate words wherever it finds a space, a tab, or a newline. Each piece becomes its own separate argument to the command.
So touch $name does not always mean touch one thing. The shell expands $name, then splits the result on spaces, and hands touch however many pieces it found. One word in the variable, one argument. Three words, three arguments. The command never sees your intent, only the words the shell handed it.
That chopping is word splitting. It happens after expansion, only on UNQUOTED values, and it is the whole lesson.
The shell does not guess where to cut. It reads a setting called IFS, short for Internal Field Separator. IFS is just a variable that holds the characters the shell treats as gaps between words. By default it holds three: a space, a tab, and a newline. Those are the everyday whitespace characters, which is why word splitting feels like splitting on spaces.
You can see those three characters if you print IFS through a tool that spells out invisible characters. Do not worry about the tool itself; the point is only what comes back. Run it and read the three tokens:
printf '%s' "$IFS" | cat -A
prompt: student@linuxcamp:~$ answer: printf '%s' "$IFS" | cat -A output: ^I$ hint: Print IFS and pipe it to cat -A, which makes hidden characters visible: printf '%s' "$IFS" | cat -A
Three characters, made visible. The plain space at the front is the space. ^I is how cat -A draws a TAB. And $ is how it marks the end of a line, which is the newline. So IFS holds space, tab, newline: exactly the whitespace the shell splits unquoted values on. That default is why a filename with a space in it is about to cause trouble.
The cleanest way to SEE splitting is a loop. A for loop walks through a list of words one at a time, running its body once per word. You met for earlier; here it is just a window that lets you watch each word the shell produced.
Put three words in one variable, then loop over it UNQUOTED. Before you run it, commit to a guess: the variable holds one value, so does the loop run once, or does it run once for each word inside?
files="a b c"; for f in $files; do echo "[$f]"; done
prompt: student@linuxcamp:~$ answer: files="a b c"; for f in $files; do echo "[$f]"; done output: [a] [b] [c] hint: Set files to "a b c", then loop over $files with no quotes, echoing each in brackets.
Three lines, not one. The variable held a single string, a b c, but because $files was unquoted, the shell split it on its spaces into three separate words before the loop ever started. The loop then ran three times, once for a, once for b, once for c, and the brackets around each prove they arrived as three clean, separate words. That is word splitting doing exactly what it does, and here it is useful. In the next step it is anything but.
Now the trap that catches everyone. Real filenames have spaces in them: my file.txt, Final Report.pdf, New Folder. Watch what the same splitting rule does to one.
Store a filename that contains a space, then try to create it with touch WITHOUT quoting the variable. Before you run it, commit to a guess: you named one file, but the value has a space in it, so how many files will touch actually make?
name="my file.txt"; touch $name; ls
prompt: student@linuxcamp:~$ answer: name="my file.txt"; touch $name; ls output: file.txt my hint: Set name to "my file.txt", then run touch $name with no quotes, then ls to see what got made.
Look closely: you asked for my file.txt and got two files instead, my and file.txt. The shell expanded $name to my file.txt, split it on the space into two words, and handed both to touch, which dutifully created one file per word. No error, no warning. This is the number-one filename bug for beginners, and the command looked completely reasonable.
Here is the whole cure, and it is one keystroke on each side. Wrap the expansion in double quotes, "$name", and the shell still expands the variable but skips the splitting step entirely. The value stays as one word, spaces and all.
Create the same file, this time with the variable quoted. Before you run it, commit to a guess: with the value kept whole, how many files will you get now?
name="my file.txt"; touch "$name"; ls
prompt: student@linuxcamp:~$ answer: name="my file.txt"; touch "$name"; ls output: 'my file.txt' hint: Same as before, but wrap the variable in double quotes: touch "$name", then ls.
One file, exactly as named. ls prints it as 'my file.txt', wrapping it in quotes to show you the name really does contain a space, but it is a single file. The only thing that changed from the broken version was the two double quotes around $name. That is the rule in one line: double-quoting an expansion, "$var" or "$(cmd)", turns word splitting OFF and keeps the value as one word. Leave it unquoted and the shell splits it on IFS. When in doubt, quote it.
One honest wrinkle, because you will notice it and wonder. If you echo a spaced value with and without quotes, the two often look identical. That is because echo prints its arguments with a single space between them, so after the shell splits my file.txt into two words, echo just rejoins them with a space and you see my file.txt either way. The splitting happened; echo hid it by putting the space back.
To actually SEE the difference, use a value with RUNS of spaces. Splitting throws the extra spaces away; quoting keeps them. Run the unquoted form first:
spaced="one two three"; echo $spaced
prompt: student@linuxcamp:~$ answer: spaced="one two three"; echo $spaced output: one two three hint: Set spaced to a value with several spaces between words, then echo it with no quotes.
The runs of spaces collapsed to one. Unquoted, the shell split one two three on its whitespace into just three words, one, two, three, throwing away every extra space in between. Then echo printed those three words with one space each. The original spacing is gone, destroyed by the split. Now do it quoted and watch the spaces survive.
Same value, same echo, but this time wrap it in double quotes. The quotes switch off splitting, so the shell hands echo the whole string as one word, every space intact.
spaced="one two three"; echo "$spaced"
prompt: student@linuxcamp:~$ answer: spaced="one two three"; echo "$spaced" output: one two three hint: Same value as before, but put double quotes around the variable: echo "$spaced".
Every space came through. Because "$spaced" was quoted, the shell never split it, so echo received one single argument, the full string with all its spacing, and printed it back exactly. Set the two side by side: unquoted gave you one two three, quoted gave you one two three. Same variable, same command, and the only difference was a pair of double quotes. That IS word splitting, made visible.
Scaffolding off. No command is printed this time. You have every piece you need.
A variable named doc already holds the value Final Report.pdf, a filename with a space in the middle. Create exactly ONE file with that name, using the variable. Remember what an unquoted expansion would do to that space, and choose your quoting so you get one file, not two.
prompt: student@linuxcamp:~$ answer: touch "$doc" output: hint: The value has a space, so an unquoted $doc would split into two words. Wrap the expansion in double quotes so it stays one word: touch "$doc".
The answer is touch "$doc", with the variable in double quotes. Unquoted, touch $doc would have expanded to Final Report.pdf, split it on the space, and made two files, Final and Report.pdf, exactly the bug from earlier. The double quotes turned splitting off, so touch received one argument and made one file. You recalled the fix from the rule alone, which is the same move the real machine will ask of you every time a path has a space in it.
Word splitting does not only make extra files. It also breaks commands that READ. If you point a command at an unquoted variable holding a spaced path, the shell splits it and the command goes looking for the wrong names.
Picture running cat my report.txt where my report.txt was meant to be one filename. The space splits it into two words, so cat tries to read two files that do not exist:
cat: my: No such file or directory
cat: report.txt: No such file or directory
Two errors, one per split word, and neither name is the file you meant. The moment you see a No such file or directory naming half of a filename you recognise, the cause is almost always an unquoted expansion. The fix is the same one keystroke on each side: quote it, cat "$path".
You earned this cheat sheet. Every row is a fact you watched happen:
The rule to carry for the rest of your Linux life: when a value might contain a space, a tab, or a newline, and almost every filename and path can, wrap the expansion in double quotes. "$name", "$path", "$(some command)". Quote by default and word splitting stops surprising you.
IFS is a variable, so it CAN be changed. Engineers sometimes set it to a comma to split a line of comma-separated values into fields, then set it back. That is an advanced move for later. The takeaway now is the reverse: because IFS defaults to whitespace, unquoted values split on spaces, and double quotes are how you opt out.
The practice terminal has shown you the whole rule. The shell expands a variable, then, if it was not quoted, splits the result into words on IFS: space, tab, and newline. You watched one variable become three loop words, watched a spaced filename split into two files, and watched double quotes put a stop to it. Every command you typed yourself.
The Bash Environment module ends with one real Linux machine, the Bash Environment capstone mission. A VM boots just for you, with its own shell, and it hands you objectives that use exactly what you practiced across this module: variables, expansions, quoting, and the splitting rule you just learned. One difference from here: the mission shows no commands. You read the objective, you recall the quoting, you type it. That recall is what makes it stick.
Finish the other Bash Environment lessons, then go quote a real filename for yourself.
Practice Word Splitting in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.