10

Looking to find all files (recursively) which have an underscore in their file name and then delete them via command line.

16

This is the safest and fastest variant:

find /path -type f -name '*_*' -delete

It does not require piping and doesn't break if files contain spaces or globbing characters or anything else that other constructs would choke on. The easiest rule to remember here is to never parse find output. And never grep on filenames if you want to do something with them later. You can do almost anything with find directly.

See also: GNU find Manual – Deleting Files

  • In all these years I never noticed the -delete option. Thanks. – James Roth Jan 9 '14 at 15:33
2

This includes directories which are considered files. Some of the other examples using xargs will fail if the filename contains spaces.

find . -name '*_*' -exec rm -rf {} \;

If you only want regular files:

find . -type f -name '*_*' -exec rm -f {} \;
0

Alright, let's do this progressively.

As a first pass, this is just a simple exercise in passing a wildcard to the find command, remembering to quote it of course, and executing the rm command for every file found:

find ${BASE_DIR}/ -name '*_*' -exec rm {} \;

But of course that's dreadfully inefficient. It starts up a whole rm process for each individual file. So while we could take a short detour through \+ that's not where we are going to end up, so let's take the shorter route and bring in xargs to batch up the filenames into groups:

find ${BASE_DIR}/ -name '*_*' -print | xargs rm

But that has two security holes. First, if any filename found happens to begin with a minus sign rm will treat it as a command-line option rather than a filename, and generate an error. (The -exec rm {} version also has this problem.) Second, filenames containing whitespace will not be handled properly by xargs. So a further iteration is to make this a little more bulletproof:

find ${BASE_DIR}/ -name '*_*' -print0 | xargs -0 rm --

And, of course, there are the interactive features of rm that you probably don't want:

find ${BASE_DIR}/ -name '*_*' -print0 | xargs -0 rm -f --

The -print0 and -0 options are not standard, but the GNU find and xargs, as well as the FreeBSD find and xargs, understand them. However, even this is improvable. We don't need to spawn any extra processes at all. The GNU and FreeBSD finds can both invoke the unlink(2) system call directly:

find ${BASE_DIR}/ -name '*_*' -delete

As a last preventative measure to stop you doing more than you intended in certain circumstances, remember that the filesystem can contain more than just regular files:

find ${BASE_DIR}/ -name '*_*' -type f -delete
-2

find . -type f -regex ".*_.*" | xargs rm -f

explanation
-type f finds only files
-regex ".*_.*" selects only files that have an underscore in them
xargs rm -f then removes the selected file forcefully (never prompts before deleting)

  • 1
    To handle files with spaces: find . -type f -regex ".*_.*" -print0 | xargs -0 rm -f, but then it still doesn't handler directories – James Roth Jan 9 '14 at 15:42

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.