4

I have folder named play which contains a file damn file (with space). When I use find and pipe the output to tar:

find play/ -name 'damn*' | tar cf archive.tar -T -

It works perfectly. Why does this works The output of find contains space that should cause problem for tar. When I use this command:

tar cf archive.tar $(find play/ -name 'damn*')

bash shows these errors:

tar: play/damn: Cannot stat: No such file or directory
tar: file: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors

I'm confused. Why does the first example work?

Majid Azimi
  • 3,098

2 Answers2

8

In the second example, the newlines are removed by the shell, which does word-splitting on the output of the command substitution. Since newlines are included in IFS by default, they are treated as field delimiters and not as part of the fields themselves. In the first example, the input is coming from a pipe and not the shell so the shell can't do word-splitting.

Double quoting the command substitution ("$(find ... )") will prevent word splitting from taking place, but find inside command substitution is almost always a bad idea so don't do that. For maximum safety, whenever you have access to the GNU versions of Unix tools you should use the null-delimiter options:

find dir/ -name '...' -print0 | tar cf foo.tar --null --no-recursion -T -
jw013
  • 51,212
4

find play/ -name 'damn*' will produce a list of filenames, 1 per line. If play/damn file is the only matching file, then it's equivalent to echo 'play/damn file'.

Tar's -T option expects filenames to be separated by newlines. Any other whitespace is considered part of the filename. That's why your first example works.

When you use $(...), the output of the command is split on whitespace by the shell, and then each word becomes a parameter to tar. Thus, play/damn and file become two arguments, and neither one is an existing file.

cjm
  • 27,160