Skip to content

/flags/find/find-print0-xargs-0

find -print0 | xargs -0: handle file names with spaces and newlines

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · find (GNU findutils) 4.11.0updated
tool
find
command
find
flag
-print0
default
off (-print ends each path with a newline)

find . -name '*.txt' -print0 | xargs -0 wc -l passes every file name to wc intact, even names with spaces, quotes or newlines. -print0 prints each path followed by a NUL byte instead of a newline, and xargs -0 (--null) splits its input on NUL bytes only and treats quotes and backslashes as ordinary characters. NUL is the only byte that cannot appear in a Unix file name, so the pair is always unambiguous.

Without and with -print0

bash
mkdir -p proj
touch proj/a.txt 'proj/my file.txt'
echo '-- newline separated'; find proj -name '*.txt' | xargs ls 2>&1 | sort
echo '-- NUL separated'; find proj -name '*.txt' -print0 | xargs -0 ls | sort
echo '-- NUL shown as |'; find proj -name '*.txt' -print0 | sort -z | tr '\0' '|'; echo

Output

-- newline separated
ls: cannot access 'file.txt': No such file or directory
ls: cannot access 'proj/my': No such file or directory
proj/a.txt
-- NUL separated
proj/a.txt
proj/my file.txt
-- NUL shown as |
proj/a.txt|proj/my file.txt|

Plain xargs splits proj/my file.txt into proj/my and file.txt, and ls fails on both. With -print0 and -0 the name arrives as one argument.

Characters that break the newline form

Name containsfind | xargsfind -print0 | xargs -0
space or tabsplit into several argumentsone argument
single quote (don't.txt)xargs: unmatched single quoteone argument
double quote or backslashquote removed or errorone argument
newlinesplit into two namesone argument
leading dash (-x.txt)safe with find . (names start with ./)same: -0 does not change the name itself

The single-quote case is the xargs: unmatched single quote error.

Useful companions

OptionToolWhat it does
-0, --nullxargsInput items end with NUL; quotes and backslashes are literal
-r, --no-run-if-emptyGNU xargsDo not run the command if find printed nothing
-n NxargsAt most N names per command invocation
-P NGNU/BSD xargsRun up to N commands in parallel
-zGNU grep, sortRead and write NUL-separated lines, so pipelines keep the NUL format
read -r -d ''bashRead one NUL-terminated name per loop iteration
bash
mkdir -p loop
touch 'loop/one two.txt' loop/three.txt
find loop -type f -print0 | sort -z | while IFS= read -r -d '' f; do printf '[%s]\n' "$f"; done

Output

[loop/one two.txt]
[loop/three.txt]

-print0 or -exec ... +

find ... -exec cmd {} + gives the same safety without a pipe and works with every POSIX find; see find -exec + vs ;. The pipe form is worth it when you need xargs features such as -P for parallel runs or -n to control batch size. Either way the command receives as many names per invocation as fit in the system limit, which avoids Argument list too long.

Pitfalls

  • -print0 must be the action: in find . -name a -o -name b -print0 it applies only to the second test. Group the tests: find . \( -name a -o -name b \) -print0.
  • Every program in the pipe must understand NUL: head, grep without -z, or xargs without -0 will mangle it.
  • On Git Bash, names still follow Windows rules (no : or *), but spaces are common, so the pair matters there too.

FAQ

Why does xargs say unmatched single quote?

Without -0, xargs splits on blanks and interprets quotes and backslashes, so a name such as don't.txt breaks it. Use find -print0 | xargs -0, which passes names unchanged.

Is -print0 portable?

GNU, BSD/macOS and BusyBox find all support it, and POSIX.1-2024 added find -print0 and xargs -0. Older strictly POSIX systems may lack it; find -exec cmd {} + works everywhere.

How do I read -print0 output in a bash loop?

Use while IFS= read -r -d '' f; do ...; done < <(find . -type f -print0). The empty -d '' makes read stop at each NUL byte.

Sources