Learn › Linux Foundations › Arguments
xargs - a hands-on Linux lab on a real virtual machine.
The whitespace trap left five files counting as seven tokens. Fix it for good with the NUL byte: find -print0 with xargs -0 (and its long spelling --null) keeps every filename whole, proven by the counter dropping back to 5 and real wc -l work on every file.
Remember where the xargs Quoting lesson left you. xargs splits its input on whitespace, and the space inside a filename like final draft.txt looks to it exactly like the space between two separate items. So the five files under files/ were torn into seven tokens, and any command you ran on that list would have hit two files that do not exist and missed two that do.
You are back in ~/args-base, the same folder as before: words.txt next to a files/ folder of five files, two of which carry a space in the name (final draft.txt and q1 report.txt). Start by re-running the counter that exposed the bug. Quick reminder of how it works: xargs -n1 prints one token per line, and wc -l counts lines, so the number it prints is how many arguments xargs actually saw. Before you run it, decide: five real files, so what number do you expect?
find files -type f | xargs -n1 | wc -l
prompt: student@linuxcamp:~/args-base$ answer: find files -type f | xargs -n1 | wc -l output: 7 hint: The counter from the xargs Quoting lesson, every pipe padded: find files -type f | xargs -n1 | wc -l
Still 7. Nothing has changed: two space-names each split into two tokens, so five files count as seven. Sorting cannot fix this, quoting the command cannot fix this, because the problem is the separator. As long as items are separated by whitespace, whitespace inside a name is indistinguishable from whitespace between names. There is exactly one honest way out, and it is what this lesson is about.
The fix is to stop separating filenames with whitespace and separate them with a character that can never appear inside a filename: the NUL byte, a single zero byte. Because no filename may ever contain a NUL, a NUL between items can only ever mean "end of this item." No ambiguity, no splitting inside names.
Two options team up to make that happen:
find ... -print0 ends each path it prints with a NUL instead of a newline.xargs -0 splits its input ONLY on NUL, never on spaces or newlines.The producing side speaks NUL, the consuming side listens for NUL, and everything in between (spaces, newlines, tabs) is just part of the name.
The NUL byte is character number zero. Unix has used it since the beginning to mark the end of a string inside the kernel, which is exactly why the kernel refuses to allow it inside a filename. GNU findutils added -print0 and -0 so the two tools could exploit that guarantee together.
Chain it together. One extra tool joins in: sort -z, which is the sort you know with -z telling it that items end in NUL too, so the order stays stable for practice (remember from the Quoting lesson: on its own, find prints in filesystem order, which varies from machine to machine). Then xargs -0 -n1 echo prints each argument it saw on its own line, exactly the probe that exposed the bug last lesson.
Before you run this, decide: the broken version printed seven lines. How many will the NUL-safe version print?
find files -type f -print0 | sort -z | xargs -0 -n1 echo
prompt: student@linuxcamp:~/args-base$ answer: find files -type f -print0 | sort -z | xargs -0 -n1 echo output: files/data.csv files/final draft.txt files/notes.txt files/q1 report.txt files/report.txt hint: Add -print0 to find, sort -z, then xargs -0 -n1 echo: find files -type f -print0 | sort -z | xargs -0 -n1 echo
Five lines, and look at lines two and four: files/final draft.txt and files/q1 report.txt, each on ONE line, spaces and all. That is the delta that matters. Last lesson those two names each shattered across two lines; now the NUL separators tell xargs exactly where each name ends, so the spaces inside them mean nothing. This is the safe pattern for any list of filenames: -print0 on the producing side, -0 on the xargs side.
One matched pair of runs is worth more than any explanation. Rebuild the token counter from the cold open, but the NUL-safe way: -print0 on find, -0 on xargs. No sort needed, because wc -l only counts lines and does not care about order.
Before you run it, commit to a number: the broken counter said 7. What will this one say?
find files -type f -print0 | xargs -0 -n1 | wc -l
prompt: student@linuxcamp:~/args-base$ answer: find files -type f -print0 | xargs -0 -n1 | wc -l output: 5 hint: Same counter as the cold open but with -print0 and -0: find files -type f -print0 | xargs -0 -n1 | wc -l
5, exactly the number of real files. Put the two runs side by side: same five files, same counter, 7 without NUL and 5 with it. The gap between those two numbers WAS the bug, and it is now closed. That before-and-after pair is the cleanest proof of what -print0 and -0 buy you.
Stop and notice what you can now do that you could not do one lesson ago. You can take ANY list of filenames, no matter how awkward the names, and deliver every one of them to a command in one piece. Most people who use xargs for years never learn this and get quietly wrong results the day a space-name shows up. You measured the breakage (7), applied the fix, and measured the repair (5). That is engineering, not recipe-following.
One more spelling to file away: -0 has a long name, --null. find files -type f -print0 | sort -z | xargs --null -n1 echo behaves identically to the -0 version. You will meet --null in scripts written by people who prefer options that read like sentences.
Echoing names back is a probe, not a job. The point of the safe chain is that ANY command can now run on every file without a single name breaking. wc -l reports how many lines are in each file it is handed, so make xargs run it on all five at once. Before you run it, decide: how many files will appear in the report, and will the two space-names be among them?
find files -type f -print0 | sort -z | xargs -0 wc -l
prompt: student@linuxcamp:~/args-base$ answer: find files -type f -print0 | sort -z | xargs -0 wc -l output: 2 files/data.csv 1 files/final draft.txt 1 files/notes.txt 1 files/q1 report.txt 1 files/report.txt 6 total hint: Same safe chain, but xargs runs wc -l instead of echo: find files -type f -print0 | sort -z | xargs -0 wc -l
All five files reported, including both space-names, each counted correctly because -0 kept each name a single argument. Notice the counts are right-aligned with a leading space in front of the single digits; that spacing is wc lining the numbers up in a column, and it is part of the real output, along with the 6 total summary line at the bottom. This is the payoff: once your list is NUL-safe, any command you like, wc, rm, cp, runs correctly on every file, awkward names and all.
Three goals, no commands shown. Everything you need you ran in this lesson. Build each pipeline yourself.
Challenge 1. Print the five file paths under files/, one per line, in sorted order, with both space-names intact on their own single lines.
prompt: student@linuxcamp:~/args-base$ answer: find files -type f -print0 | sort -z | xargs -0 -n1 echo output: files/data.csv files/final draft.txt files/notes.txt files/q1 report.txt files/report.txt hint: Three links in the chain: find with its NUL printer, the NUL-aware sort, then xargs told to split on NUL and echo one argument per run.
Challenge 2. Prove, with a single number, that no filename is being split: make the token count come out equal to the real file count.
prompt: student@linuxcamp:~/args-base$ answer: find files -type f -print0 | xargs -0 -n1 | wc -l output: 5 hint: The cold-open counter, upgraded: NUL out of find, NUL into xargs, one token per line, count the lines. No sort needed.
Challenge 3. Produce the same five-line sorted list as Challenge 1, but spell the NUL option on xargs the long way instead of -0.
prompt: student@linuxcamp:~/args-base$ answer: find files -type f -print0 | sort -z | xargs --null -n1 echo output: files/data.csv files/final draft.txt files/notes.txt files/q1 report.txt files/report.txt hint: Identical chain to Challenge 1, but swap the short NUL flag on xargs for its two-word long name from the milestone step.
You earned this cheat sheet. Every row is something you just ran on ~/args-base:
The one rule that ties it together: whitespace separators break on names that contain whitespace, so separate filenames with NUL instead. -print0 on the producing side, -0 (or --null) on the xargs side. That single habit turns xargs from a footgun into a tool you can trust on any list of files.
Reach for -print0 and -0 by reflex, even when today's names look tidy. The one file someone adds next month with a space in it is exactly the one that would have broken a naive pipeline, and the NUL-safe version simply keeps working.
You re-measured the Quoting lesson's bug (7), learned why NUL is the one separator no filename can sabotage, applied -print0 with -0 to bring the count back to 5, ran real work across every file with wc -l, and met --null, the long spelling. Then you rebuilt all of it from bare goals in the challenges. One thing waits in the next lesson: xargs -P, which runs several commands at once. Full lesson coming.
This module ends with one real Linux machine: the arguments mission lab. There a VM boots just for you on this same words.txt and files/ fixture, and the mission hands you objectives that use exactly what you just ran, showing no commands. You read the objective, recall the option, and type it. That recall is what makes it stick.
Finish the other arguments lessons, then go build argument lists for real.
Practice xargs Null-Delimited: the -0 fix in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.