How do I recursively view a list of files that has one string and specifically doesn\'t have another string? Also, I mean to evaluate the text of the files, not the filenames.
Try this:
grep -rl <string-to-match> | xargs grep -L <string-not-to-match>
Explanation: grep -lr
makes grep recursively (r) output a list (l) of all files that contain <string-to-match>
. xargs loops over these files, calling grep -L
on each one of them. grep -L
will only output the filename when the file does not contain <string-not-to-match>
.
These answers seem off as the match BOTH strings. The following command should work better:
grep -l <string-to-match> * | xargs grep -c <string-not-to-match> | grep '\:0'
To match string A and exclude strings B & C being present in the same line I use, and quotes to allow search string to contain a space
grep -r <string A> | grep -v -e <string B> -e "<string C>" | awk -F ':' '{print $1}'
Explanation: grep -r recursively filters all lines matching in output format
filename: line
To exclude (grep -v) from those lines the ones that also contain either -e string B or -e string C. awk is used to print only the first field (the filename) using the colon as fieldseparator -F
find . -maxdepth 1 -name "*.py" -exec grep -L "string-not-to-match" {} \;
This Command will get all ".py" files that don't contain "string-not-to-match" at same directory.
Here is a more generic construction:
find . -name <nameFilter> -print0 | xargs -0 grep -Z -l <patternYes> | xargs -0 grep -L <patternNo>
This command outputs files whose name matches <nameFilter>
(adjust find
predicates as you need) which contain <patternYes>
, but do not contain <patternNo>
.
The enhancements are:
If you don't need to filter by name (one often wants to consider all the files in current directory), you can strip find
and add -R
to the first grep
:
grep -R -Z -l <patternYes> | xargs -0 grep -L <patternNo>
The use of xargs in the answers above is not necessary; you can achieve the same thing like this:
find . -type f -exec grep -q <string-to-match> {} \; -not -exec grep -q <string-not-to-match> {} \; -print
grep -q
means run quietly but return an exit code indicating whether a match was found; find
can then use that exit code to determine whether to keep executing the rest of its options. If -exec grep -q <string-to-match> {} \;
returns 0, then it will go on to execute -not -exec grep -q <string-not-to-match>{} \;
. If that also returns 0, it will go on to execute -print
, which prints the name of the file.
As another answer has noted, using find
in this way has major advantages over grep -Rl
where you only want to search files of a certain type. If, on the other hand, you really want to search all files, grep -Rl
is probably quicker, as it uses one grep
process to perform the first filter for all files, instead of a separate grep
process for each file.