我有一个名为“labels”的目录,其中有文本文件,其中包含“cat”或“dog”或两者的标签,位于不同的行上。
labels目录中的文件内容为:
cat labels/1.txt
cat
cat labels/2.txt
dog
cat labels/3.txt
cat
dog
我想获取仅包含标签“cat”的文件的名称。我尝试了以下命令:
ls labels | grep -Rwl "cat"
labels/1.txt
labels/3.txt
但此命令返回包含“cat”或两者的文件的名称。但我的要求是获取那些仅包含“cat”的文件名,而不是同时包含“cat”和“dog”。
同样,当我尝试获取仅包含“dog”的文件的名称时。如果我以相同的方式搜索,那么它会返回包含“dog”或两个标签的文件名。
ls labels | grep -Rwl "dog"
labels/2.txt
labels/3.txt
答案1
您可以使用grep
两次:a) 列出所有带有 的文件cat
,然后 b) 筛选出dog
包含 的文件。分别使用-l
和-L
,其中-l
列出匹配的文件名和-L
不匹配的文件名:
grep -L 'dog' $(grep -l 'cat' <list of files>)
看man grep
:
-L, --文件不匹配
抑制正常输出;相反,打印每个通常不会打印输出的输入文件的名称。扫描将在第一个匹配处停止。
-l, --带匹配的文件
抑制正常输出;相反,打印通常会打印输出的每个输入文件的名称。扫描将在第一个匹配处停止。
答案2
使用 GNUgrep
和xargs
(无论如何,-R
您已经使用的是 GNU扩展,尽管在那里更好):grep
-r
grep -rwlZ cat labels/ | xargs -r0 grep -wL dog
将列出至少包含一个cat
单词且不包含dog
任何单词的文件 (单词在这种情况下,意思是:“没有被包围单词字符”,单词字符是字母数字字符和下划线)。替换-w
为-x
以搜索其全部内容的行是 cat
/ dog
。
答案3
如果您想列出包含“cat”但不包含“dog”的文件的名称,请尝试使用find
和 GNU awk
(或任何其他awk
支持ENDFILE
块的文件,因为这是 的 GNU 扩展awk
):
$ find labels/ -type f -exec awk -v IGNORECASE=1 '
/\<cat\>/ { cat = 1 };
/\<dog\>/ { dog = 1 };
ENDFILE {
if (cat == 1 && dog == 0) {
print FILENAME
};
cat = 0;
dog = 0;
}' {} +
labels/file1.txt
或者你可以使用perl
而不是awk
:
$ find labels/ -type f -exec perl -l -n -e '
$cat = 1 if m/\bcat\b/i;
$dog = 1 if m/\bdog\b/i;
if (eof) {
print $ARGV if ($cat && ! $dog);
$cat=0;
$dog=0;
}' {} +
labels/file1.txt
上述 awk 和 perl 版本的输出是labels/
使用子目录中的以下文件生成的:
$ tail labels/*
==> labels/file1.txt <==
cat
==> labels/file2.txt <==
dog
==> labels/file3.txt <==
cat
dog
labels/file1.txt
是唯一打印的文件名,因为它是唯一包含“cat”且不包含“dog”的文件。
答案4
for f in *; do diff -q <(sort -u "$f") <(echo cat) >/dev/null && echo "$f"; done