-1

From findutils' manual about xargs

In many applications, if xargs botches processing a file because its name contains special characters, some data might be lost.

However, here is an extreme example of the problems that using blank-delimited names can cause. If the following command is run daily from cron, then any user can remove any file on the system:

find / -name ’#*’ -atime +7 -print | xargs rm

For example, you could do something like this:

eg$ echo > ’#
vmunix’

and then cron would delete /vmunix, if it ran xargs with / as its current directory.

To delete other files, for example /u/joeuser/.plan, you could do this:

eg$ mkdir ’#
’
eg$ cd ’#
’
eg$ mkdir u u/joeuser u/joeuser/.plan’
’
eg$ echo > u/joeuser/.plan’
/#foo’
eg$ cd ..
eg$ find . -name ’#*’ -print | xargs echo
./# ./# /u/joeuser/.plan /#foo

In the above examples, how does xargs work on the filenames provided by find, and how can that delete files?

Thanks.

4

The problem is in how xargs reads the input and passes it onto the command.

We can see this pretty easily with rm -i

$ touch a 'b c' 'd
> e'
$ find -type f | cat
./d
e
./b c
./a
$ find -type f | xargs rm -i
rm: cannot remove './d': No such file or directory
rm: cannot remove 'e': No such file or directory
rm: cannot remove './b': No such file or directory
rm: cannot remove 'c': No such file or directory
rm: remove regular empty file './a'? $ 

Note that we try to remove 5 files; ./a, ./b, c, ./d, and e

So both the space and the newline in the filename causes two arguments to be passed to the command.

The standard solution is to use find -print0 and xargs -0

$ find -type f -print0 | xargs -0 rm -i
rm: cannot remove '.': Is a directory
rm: remove regular empty file './d\ne'? 
rm: remove regular empty file './b c'? 
rm: remove regular empty file './a'? $ 

Now the NUL character (which can't be part of a filename) is used as the separator and the whitespace isn't significant.

  • -print0 xand xargs -0 are not standard. find . -type f -exec rm -i {} + is standard (and doesn't have the xargs issue that rm -i could read the answer to its prompts from the pipe from find with some xargs implementations). Note that with GNU xargs, you'd need xargs -r0 for the equivalent of that -exec {} +. – Stéphane Chazelas Jun 9 '18 at 17:08
  • I was just using rm -i to highlight how xargs was splitting stuff up, and so showing how rm could delete unexpected files as a result. As you point out, the -i doesn't work properly because of the pipeline. – Stephen Harris Jun 9 '18 at 18:34
1

The other half of the question, is what to do about it. The answer to this is to use \0 delimiting instead of white space delimiting (-0 in not allowed in a file-name, it is the only character that is not allowed. / is part of a filename but has a special purpose).

To do this change the code as follows.

find … -print0 | xargs -0 …

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.