3

I have a large application and I need to find a list of files that do not contain an expression. I have been using The Silver Searcher but I am unsure how to get the opposite

Example ag 'breadcrumbs' app/controllers This gives a list of all the files and line number where I can find this expression in the controllers' directory

What I am trying to get is a list of all the files that do not have breadcrumbs

I am open to bash or shell scripts as well

Thanks for the help

MZaragoza
  • 10,108
  • 9
  • 71
  • 116

3 Answers3

4

For The Silver Searcher (ag), ag -vl breadcrumbs directory_here will do what you're looking for.

-l lists files, and -v inverts the match to find files that don't match.

You could also do ag -L breadcrumbs directory_here

-L lists all files that do not contain a match.

Ryan M
  • 18,333
  • 31
  • 67
  • 74
  • That's what `grep` should do when `-v` is combined with `-l`. Unfortunately, `-v` inverts the lines which match, even when the reporting style calls for a list of files and not individual lines. This silly requirement is very old and controlled by POSIX. – Kaz Nov 22 '19 at 00:36
  • `-L` was what I needed, `-l -v` was doing what @Kaz was complaining about (`ag` on Debian11) – Hashbrown Jan 31 '22 at 04:48
1

With recursive grep and sed we can do this:

grep -cr <pattern> <directory> | sed -n s/:0$//p

Explanation: the -c option to grep will cause it to emit the matching file names along with a count of the number of matches separated by a colon. The names of files which don't have a match are therefore suffixed with :0.

In the next stage of the pipeline, we use sed. The -n option tells it not to print all the lines that pass through it, which means that only those lines will print that we tell it to. We use sed's s command to edit lines ending in :0 to delete that suffix. At the same time, we add the p command to have these matching lines printed.

Kaz
  • 55,781
  • 9
  • 100
  • 149
-2

Use grep -vl:

find myprojectroot -type f | xargs grep -vl breadcrumbs

grep search file using regex, but plaintext works too.

-v matches only when the term isn’t found -l lists the file (instead of each match)

xargs executes a command, passing each line on the the command

find X -type f finds all files in or under the directory named X (-type d finds directories, which you don’t want)

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 1
    This is unfortunately incorrect. `grep -v breadcrumbs` finds all **lines** in the input which do not match `breadcrumbs`. If any such lines are found, then `-l` will list the file. For instance, if I have a C program that contains `main` then both `grep -l main prog.c` and `grep -lv main prog.c` will output `prog.c` -- unless the program contains exactly one line. – Kaz Nov 22 '19 at 00:33
  • @kaz no, this *is* correct, and no it does not find all lines that do not match - it returns the file names of those files that do not contain `breadcrumbs` *anywhere* in the file. To be clear, if `breadcrumbs` appears on only 1 line of a multi-line file, then the file name is not in the output. I tested my answer before posting it. Note that my answer has nothing to do with `ag` - it is a pure-bash solution. If you are usins this with/in `ag` I don't know if it would work or not. – Bohemian Nov 22 '19 at 02:31
  • writes *> "it returns the file names of those files that do not contain breadcrumbs anywhere in the file."* That is indisputably true; however, the problem is that the output is not restricted to just those files. It also includes all files that have at least one line that doesn't match `breadcrumbs` (even if they contain a match for `breadcrumbs`). – Kaz Nov 22 '19 at 02:47
  • For instance, make a file that contains two lines, `a` and `b`. Now if you type `grep -lv c file`, then surely enough, it is listed. And, surely enough, it is not listed if we issue `grep -l c file`. But, the problem is that the file **is** listed by both `grep -lv a file` and `grep -l a file`! This is because the line `b` is a match for `-v a`, the inverted `a` pattern. And thus since there is a match, the file is included in the output. – Kaz Nov 22 '19 at 02:50
  • @kas sorry, but you are wrong. I am using the bash shell and it work as I have described - a hit on any line prevents output (even if there are lines that don’t contain the match). When grep scans files, it’s an “all or nothing” result. I literally just ran it to prove it and it absolutely works like this. Have you actually run my command? If so, then you must be using some weird version of bash or CLI tools. – Bohemian Nov 22 '19 at 02:55
  • 1
    I tested with GNU grep 2.10 on Ubuntu 12, and with GNU grep 3.1 on Ubuntu 18. I also checked the [POSIX spec](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html) which makes it clear that `-v` selects non-matching **lines**, and that `-l` lists the files containing selected lines. – Kaz Nov 22 '19 at 06:08
  • @Bohemian `seq 1 2 | grep -l 1 -; seq 1 2 | grep -vl 1 -` – jhnc Nov 22 '19 at 23:33