0

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.

1

2 Answers 2

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.

2
  • -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 {} +. Jun 9, 2018 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. Jun 9, 2018 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 …

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .