如何获取仅包含指定文本的文件的名称

如何获取仅包含指定文本的文件的名称

我有一个名为“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

使用 GNUgrepxargs(无论如何,-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

相关内容