查找包含字符串且不包含另一个字符串的文件

查找包含字符串且不包含另一个字符串的文件

假设我有一些文件,我想找到其中包含字符串但不包含另一个字符串的文件。

grep 是基于行的,诸如此类的条件grep -q printf file && grep -vq '#include <stdio.h>' file将不起作用。

我该怎么做呢?

(我使用 Debian,所以专门针对 GNU 版本工具的答案就可以了。)

答案1

grep -vl将报告至少有一行与该模式匹配的文件的名称。在这里,您需要没有任何行与模式匹配的文件。 GNU grep(在 Debian 上找到)有一个-L选项:

grep -rlZ printf . | xargs -r0 grep -FL '#include <stdio.h>'

对于任何 POSIX grep,你可以直接否定grep -q

find . -type f -exec grep -q printf {} \; \
               ! -exec grep -Fq '#include <stdio.h>' {} \; \
               -print

效率低得多,因为这意味着grep在每个常规文件上运行一到两个实例。

答案2

结合find使用bash -c而不是脚本。我们获取文件路径并将其存储到file变量中,然后将其进一步传递给其他命令。首先grep -q将检查是否存在您想要的单词/模式。使用其退出状态,&&将其传递给第二个grep -q。如果该命令未找到匹配项,则意味着未找到该字符串,因此使用其退出状态,我们将其传递给echovia||运算符。

在下面的示例中,仅file2.txt包含abra但不包含cadabra单词。

$ find -type f -exec bash -c 'file="$@";grep -q "abra" "$file"  &&  grep -q "cadabra" "$file" || echo "$file" ' sh "{}" >
./file2.txt
$ ls                                                                                                                     
file1.txt  file2.txt  file 3.txt
$ cat file1.txt
abra cadabra
$ cat file2.txt                                                                                                          
abra
$ cat file\ 3.txt                                                                                                        
abra cadabra

答案3

这很简单:

for fname in ./*.c; do
  if grep -q -F "printf" "$fname" && ! grep -q -F "#include <stdio.h>" "$fname"; then
     printf 'File "%s" needs to include stdio.h\n' "$fname"
  fi
done

这将查看当前目录中的所有 C 源文件并报告任何使用printf()但不包含stdio.h标头的文件。

不过,标头可能会间接包含在内,因此为了避免误报,您可以将代码传递给 C 预处理器,并在预处理输出中查找标头(这似乎适用于gccand clang):

for fname in ./*.c; do
  if grep -q -F "printf" "$fname" && cc -E "$fname" | ! grep -q "^#.*stdio\.h\""; then
     printf 'File "%s" needs to include stdio.h\n' "$fname"
  fi
done

答案4

如果我正确地阅读了要求,您希望所有文件匹配$PAT_INCL减去文件匹配$PAT_EXCL

从概念上讲,这只是集合减法。 Unix 中没有一个非常好的用于集合操作的标准实用程序,但是comm可以使用。

comm -23 <(grep --files-with-match "$PAT_INCL"  * | sort) \
         <(grep --files-with-match "$PATH_EXCL" * | sort)

通过仅在第二个 grep 中查找匹配文件,可以提高效率:

# Assuming filenames without whitespace
grep --files-with-match "$PAT_INCL" * | sort > incl_files
grep --files-with-match "$PAT_EXCL" $(cat incl_files) | sort > excl_files
comm -23 incl_files excl_files

相关内容