4

Let's say I have a directory called "/home/ben/files" with 100 randomly named text files. Out of the 100 text files some just contain the word "DELETEME" inside the text files.

e.g. file1.txt, file2.txt, file3.txt, file4.txt, file5.txt.....etc

How should one delete such files containing the pattern?

1
  • It sounds dangerous... Might be safer to attempt to delete files which, apart from whitespace, contain only a certain string. Commented Apr 15, 2022 at 1:02

2 Answers 2

6

With GNU grep at least, you could try:

grep -lZr "DELETEME" /home/ben/files | xargs -0 rm

Warning: that will recurse through subfolders of /home/ben/files; if you don't want that, more precautions need taking.

(Thanks for cas in the comments for suggesting using a null delimiter.)

0
4

With GNU find:

First some setup, make a directory, create 100 empty files in it, and append "DELETEME" to some of them:

mkdir files
touch files/{001..100}
for i in 001 010 020 030 040 050 065 077 088 099 ; do echo "DELETEME" >> files/$i ; done

Next, list the files containing DELETEME:

find ./files/ -type f -exec grep -q DELETEME {} \; -print
./files/050
./files/001
./files/065
./files/020
./files/040
./files/030
./files/088
./files/077
./files/099
./files/010

This prints all the files where grep -q DELETEME returned true. Note that it is important to use \; to end the -exec here (and not +), as each file needs to be tested individually (otherwise the exit code from grep -q will be for the entire batch of files it is currently being run for).

find's predicates are, by default, ANDed together, so a rough English translation of what this does is "grep AND delete any files where the previous -exec returned true".

This will work with filenames containing any valid characters, including shell metacharacters, newlines and other whitespace.

And, finally, to delete the matches, use -delete instead of -print:

find ./files/ -type f -exec grep -q DELETEME {} \; -delete

If you're using a version of find that doesn't have -delete, you can use -exec again instead:

find ./files/ -type f -exec grep -q DELETEME {} \; -exec rm {} +

We can use + with the second -exec here because the filenames don't need to be deleted individually, it's better & faster to delete them in a batch or batches.

You can, of course, use any of find's other predicates - e.g. -maxdepth 1 to prevent recursion.


BTW, if you wanted to delete files that contained only "DELETEME" and nothing else, you'd need to do it more like this:

First make sure we have at least one such file.

$ echo "DON'T DELETEME" > files/001

Print a list of files containing DELETEME plus anything else:

$ find ./files/ -type f -exec grep -q '^DELETEME$' {} \; -exec grep -vq '^DELETEME$' {} \; -print
./files/001

In this case, we're using the regexp ^DELETEME$ instead of just DELETEME - that's because we want to match only lines containing DELETEME without anything else on the line. If you want to allow optional leading and/or trailing whitespace on a line, use ^[[:space:]]*DELETEME[[:space:]]*$

Then we AND that with another -exec grep that matches files containing anything other than ^DELETEME$.

We can now move on to listing the files containing DELETEME except for those containing anything else, using ! (boolean NOT) to negate the second -exec:

$ find ./files/ -type f -exec grep -q '^DELETEME$' {} \; ! -exec grep -vq '^DELETEME$' {} \; -print
./files/050
./files/065
./files/020
./files/040
./files/030
./files/088
./files/077
./files/099
./files/010

Note that file ./files/001 is missing from this list, which is exactly what we want.

Also note the ! immediately before the second -exec predicate. It's very important and also very easy to miss, so I'm explicitly pointing it out.

In this example, the first -exec grep gets the list of files containing ^DELETEME$ , then it NOT ANDs that with files containing anything other than ^DELETEME$.

The -print can be replaced with -delete or -exec rm {} + to actually delete the files.


PS: For anything more complicated than this, I'd be inclined to write a perl script, probably using the File::Find module.

Some things are just much easier to write in a procedural language style than with an extremely long find command line with chains of predicates tied together with boolean operators (it becomes very easy to lose track of the chain of logic).

You must log in to answer this question.

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