I want to delete all files that contain the string foo. How can I do this using bash in Linux?
3 Answers
Here is a safe way:
grep -lrIZ foo . | xargs -0 rm -f -- -lprints file names of files matching the search pattern.-rperforms a recursive search for the patternfooin the given directory.. If this doesn't work, try-R.-I(capitali) causes binary files like PDFs to be skipped.-Zensures that file names are zero- (i.e., nul-)terminated so that a name containing white space does not get interpreted in the wrong way (i.e., as multiple names instead of one).xargs -0feeds the file names fromgreptorm -f, separating words by zero (nul) bytes (remember the-Zoption fromgrep).--is often forgotten but it is very important to mark the end of options and allow for removal of files whose names begin with-.
If you would like to see which files are about to be deleted, simply remove the | xargs -0 rm -f -- part, and leave off the Z option to grep.
Another user suggested something like the following, which you should not run because it is unsafe:
files=`grep foo * | cut -d: -f1` rm -f $files # unsafe, do not run it! If I have files ImportantStuff that I do not want to delete and obsolete ImportantStuff containing foo, then I lose ImportantStuff (and not obsolete ImportantStuff!) when I run this command, because $files gets broken apart at spaces when it is interpreted. It is dangerous to put a list of filenames into a scalar shell variable in this way.
- 1I tried this but got "File name too long".mpelzsherman– mpelzsherman2021-01-22 00:28:21 +00:00Commented Jan 22, 2021 at 0:28
$ find -type f -exec grep -q "foo" {} \; -exec echo rm -- {} \; This recursively searches for files containing foo. The second -exec is only run if the first exec exits successfully, i.e. if grep matches. Dryrun and remove the echo if the output seems correct.
Or alternatively
$ find -type f -exec grep -q "foo" {} \; -print and
$ find -type f -exec grep -q "foo" {} \; -delete as suggested by Lekensteyn.
- 4... which can be shortened to:
find -type f -exec grep -q 'foo' {} \; -delete(the quotes around{}are redundant,findis also able to delete files.). Note thatgrepaccepts a regular expression. If you want to search forfoo.bar, either escape this or pass the-Foption to treat the pattern as literal.Lekensteyn– Lekensteyn2013-04-20 17:06:37 +00:00Commented Apr 20, 2013 at 17:06 - any reason to use
-exec echo rm "{}" \;and not-delete?vartec– vartec2013-04-20 20:47:53 +00:00Commented Apr 20, 2013 at 20:47 - 1@vartec No, I'd use
-delete. It's just easier to demonstrate with the second-execand echo in place.Adrian Frühwirth– Adrian Frühwirth2013-04-20 20:53:12 +00:00Commented Apr 20, 2013 at 20:53 - 1It is easier to type
-print(or-lsfor more verbosity) than-exec echo rm "{}" \'. No need for external utilities.Lekensteyn– Lekensteyn2013-04-20 21:07:09 +00:00Commented Apr 20, 2013 at 21:07 - 1if you use rm {}, use rm -- {}, so that any special characters in the file name itself don't get interpolated. For example, touch '-rf /' in any directory where you have write permissions...atk– atk2013-04-21 00:15:43 +00:00Commented Apr 21, 2013 at 0:15
I'd do what Adrian says,
But I'd add word boundries arond the foo so as to not accidentaly delete files containing "food" (unless it is deleting everything containing food is what you want.)
$ find -type f -exec grep -q "\<foo\>" {} \; -delete acutally, I'd redirect the output before deleteing so I could review before deleting:
$ find -type f -exec grep -q "\<foo\>" > /tmp/list_of_files_to_delete - As soon as you are able to post comments, you should post things like that as comments.FSMaxB– FSMaxB2013-04-21 08:05:45 +00:00Commented Apr 21, 2013 at 8:05
- yes I tried first but then realised that I'm not allowed to post comments yet .Petter H– Petter H2013-04-21 14:29:34 +00:00Commented Apr 21, 2013 at 14:29
foo(e.g.myfoo.jpg), or files that contain the byte sequencefoo(which may include binary files which just so happen to contain that sequence of bytes)?