假设我有一些文件,我想找到其中包含字符串但不包含另一个字符串的文件。
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
。如果该命令未找到匹配项,则意味着未找到该字符串,因此使用其退出状态,我们将其传递给echo
via||
运算符。
在下面的示例中,仅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 预处理器,并在预处理输出中查找标头(这似乎适用于gcc
and 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