Shell – How to build space separated, escaped full paths of files recursively matching glob

filenamesquotingshell

I have a program which requires paths to files as arguments, which cannot be quoted and must not depend on working dir. Can this be done without chaining multiple xargs, tr, find, readlinks?

Example output for "*.csv" files: /home/user/a\ b.csv /home/user/my\ dir/c\ d\$2.csv

Best Answer

For passing file paths as arguments to a command, find does this on its own with its -exec option without any xargs trickery:

find /home/user -name '*.csv' -exec yourcommand '{}' +

That will find every file called *.csv in /home/user and then execute yourcommand /home/user/a\ b.csv /home/user/my\ dir/c\ d\$2.csv ... with all of the found files as arguments. Each file found is passed as a separate complete argument to the program, so there is no escaping or anything else required: yourcommand gets many arguments which are each exactly the complete file path.

Another approach is that some shells (zsh in particular) support more advanced globbing, where you could just write:

yourcommand **/*.csv

and get exactly the above effect too.


If you really do want the escaped strings themselves, rather than to run a command directly, you can use bash's built-in printf:

find /home/user -name '*.csv' -exec bash -c 'printf "%q " "$@"' dummy '{}' +

The %q format specification expands to a shell-escaped version of the string argument. With many arguments printf repeats the format string for all of them, so this produces space-terminated escaped strings of all the arguments to the bash -c command. dummy is used as the value of $0, and "$@" gives printf all the rest of the arguments, which are your (intact) file names. For your example this would output exactly /home/user/a\ b.csv /home/user/my\ dir/c\ d\$2.csv.

Related Question