5

Our web server was originally maintained by a different company. They wrote a short script to clean out cache files. Their file has several lines that read like this:

/usr/bin/find /var/www/cache/blah/ |xargs /bin/rm -f >/dev/null 2>&1

Is there any reason why they couldn't just write:

/bin/rm -f /var/www/cache/blah/*

to delete the files? I can see using find when you need a particular criteria, but I can't seem to find one in this case.

7

There are a few differences in the behavior of the command lines:

  1. The find command line would delete files recursively in subdirectories, the rm command line wouldn't. You need to consider whether or not you want to recurse.
  2. The find command line would delete all files, if possible. The rm command line might skip files based on the shell's settings like GLOBIGNORE. You need to consider whether or not there might be filenames that might be accidentally ignored in pathname expansion.
  3. The find command line would succeed for any number of files. The rm command line might fail if the pathname expansion creates a command line that is too long (bigger than the system supports). Some systems have limits on this. You need to consider how many files might need to be deleted.
  4. The find command line ignores all output messages (using the redirections to >/dev/null). The rm command line prints the output messages. You need to consider what should happen with the messages.

If these differences do not matter to you, /bin/rm -f /var/www/cache/blah/* will work for you.

If only files are to be removed, and directories are to be retained, I would actually use

/usr/bin/find /var/www/cache/blah/ -not -type d -exec /bin/rm -f -- {} + >/dev/null 2>&1

or

/usr/bin/find /var/www/cache/blah/ -type f -exec /bin/rm -f -- {} + >/dev/null 2>&1

whatever suits your purpose, both have pros and cons.

-exec command {} + works just like xargs, but is slightly more efficient. The -- prevents that rm falls over if one of the filenames starts with -. Also, xargs was used in a way that makes too many assumptions about filenames. Special characters like space would actually break the simple invocation of xargs. Something like find ... -print0 | xargs -0 would be required, and then the find -exec command {} + is just much simpler.

  • 1
    xargs will fail with filename contains special characters, find -exec .. doesn't. – cuonglm Nov 20 '14 at 6:59
  • 2
    You should also note that -not and -print0 is not POSIX. They only work in some implement like GNU find or FreeBSD find. – cuonglm Nov 20 '14 at 7:21
  • @Christian Hujer, thank you, that does explain it. In this case there aren't ever any sub directories so I found it kind of odd, but the explanation above does describe possible scenarios. – Tensigh Nov 20 '14 at 8:42
  • 1
    While the original code in the question is completely flawed from a security point of view, yours also is as it's similar for instance to CVE-2011-0441. – Stéphane Chazelas Nov 20 '14 at 16:42
  • @StéphaneChazelas: Can we prevent this with POSIX standard? – cuonglm Nov 21 '14 at 1:43
8

Some reasons I can think about why they used find + xargs:

  • Handling the case when you have too many cache files, leading to an error if you run only one rm command.

  • Globbing * does not expand hidden files.

  • Working recursively.

But this find + xargs is not efficient, since when they didn't add any filter, so find result will contain directories along with files. Running /bin/rm -f on a directory leads to an error, that's why the stderr and stdout are redirected to /dev/null. And with files those name contains special characters, the command will also fail.

An improved solution can be:

find /var/www/cache/blah -type f -exec rm -f -- {} +

This is more efficient, doing all works with find, minimum forking of rm and POSIXly.

2

The other answers miss three points:

Don't ever do this:

/usr/bin/find /var/www/cache/blah/ |xargs /bin/rm -f >/dev/null 2>&1

because when you have a file with a space in it, rm will attempt to remove two files, with surprising results.

If you insist on doing it this way, assuming you are using GNU find and xargs, you want:

/usr/bin/find /var/www/cache/blah/ -print0 | xargs -0 /bin/rm -f >/dev/null 2>&1

which zero terminates the files (and expects zero termination in xargs).

Secondly, are you trying to delete directories or not? If not, you want '-type f'. If so, you want 'rm -rf' as rm without -r will not remove a directory.

But most importantly, OP asked for a shorter method, and the answers appear to be longer. How about:

/usr/bin/find /var/www/cache/blah/ -delete

I believe -delete is a GNU extension so check your version of find supports it.

  • Don't forget that -print0 and -0 are also GNU extensions. Cleaning empty sub-directories can be done with a second find command doing a depth-first search for directories and executing rmdir with errors going to /dev/null. – Jonathan Leffler Nov 20 '14 at 14:25
  • @JonathanLeffler thanks - I've added a comment re -print0 and -0 – abligh Nov 20 '14 at 15:10

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.