Ls | grep | rm — How to format this command

greplsrm

I'm trying to delete a bunch of files in a certain directory (on Mac OS X using Terminal)

ls | grep \([1-9]\) | xargs rm

should do the trick, but it doesn't. I'm trying to delete any file with a single digit in parentheses in the filename (duplicates downloaded from the web), but it ends up doing something like this:

> rm: 520syllabus2010: No such file or
> directory rm: (3).pdf: No such file or
> directory

because it doesn't interpret the space correct. It should delete "520syllabus2010 (3).pdf"

What's the proper way of doing this?

Thanks,
Jeff

Best Answer

Short version:

rm *\([1-9]\)*

Do not pipe ls to xargs. Instead, pipe find ... -print0 to xargs -0, in order to avoid such problems.

find . -maxdepth 1 -name '*([1-9])*' -print0 | xargs -0 rm

...which can be written as:

find . -maxdepth 1 -name '*([1-9])*' -exec rm {} \;

and:

find . -maxdepth 1 -name '*([1-9])*' -delete

which can further be shortened to just rm with a wildcard.


Normally, ls and find separate file names with newlines, but xargs splits its input by newline or space, resulting in the behavior you see.

It's possible tell xargs to split only by newline, but files can have newlines in their names too. In fact, on Linux and BSD, the only disallowed character is the "null" or zero byte – so it's what you should use in such cases.

In example 2 above, -print0 tells find to separate filenames with a null byte; same for xargs -0. (A few other tools also have similar options, such as sort -z or grep -zZ.)

The "short version" uses a simple shell wildcard ("pattern" in bash manual – see section "Pathname Expansion").

Related Question