Remove all files NOT containing a pattern in the contents

How do I remove all files not containing a specified text.

I understand the solution to remove files with a specified text, but I need to remove files NOT containing a specified pattern.

The following command creates a list of files not containing "successful run":

grep -c "successful run" *.out | grep -v ":1" > err.txt

I wish to directly delete these files.

4

2 Answers

Your original approach to identify appropriate files

grep -c "successful run" *.out | grep -v ":1"

is flawed. Main concerns:

  • What if there are two or more matching lines in one file? grep ":0" seems to fit the condition in the title of your question better ("files not containing a pattern").
  • What if :1 (or :0) is in the filename? So grep ':0$' seems even better.
  • What if there's a newline character in any filename passed from the first grep?

I say do not rely on grep -c; rely on the exit status from grep which is 1 if no lines were selected.

for f in *.out; do [ -f "$f" ] && { grep -q -- "successful run" "$f" [ "$?" -eq 1 ] && rm -i -- "$f" }
done

Notes:

  • [ -f "$f" ] && … will not let grep work with directories, fifos etc. that match *.out; only regular files.
  • The meaning and significance of -- is explained here. It's in case there's a file with name starting with -, it can be mistaken for an option. Another way to deal with this problem is for f in ./*.out; do …. This way every expanded $f starts with ./ so it cannot start with -.
  • I used rm -i just in case. After you test the solution you may want to omit -i.
  • grep … || rm … is worse. When a real error occurs (e.g. permission denied) the exit status of grep is greater than 1; you probably don't want to remove the file then. My solution tests if the exit status is exactly 1.
  • Your original code doesn't descend into subdirectories, neither does mine. If you want recursion then a shell that supports it (e.g. Bash with shopt -s globstar and **) or a solution based on find will be useful.

Considering that your files have either a single "successful run" or none, then:

to find the matching files you can do:

grep -l "successful run" *.out > err.txt

to find and remove them:

grep -l "successful run" *.out | xargs rm

to find the files without match you then do:

grep -vl "successful run" *.out > ok.txt

to find and remove them:

grep -vl "successful run" *.out | xargs rm

The flags mean:

-l lists the files with matches
-v/--invert-match inverts the matching logic
7

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like