嵌套管道 grep 会生成一个字符串“(标准输入)”

嵌套管道 grep 会生成一个字符串“(标准输入)”

我正在执行这样的嵌套 grep:

grep -ir "Some string" . |grep "Another string I want to find in the other grep's results"

这完全按照预期工作(我也从第二个 grep 过滤了第一个 grep 的结果),但是一旦我添加“-l”选项,那么我只从第二个 grep 获取文件列表,我不什么也得不到。

grep -ir "Some string" . |grep -l "Another string I want to find in the other grep's results"

这会产生以下输出:

(standard input)

我想当我只想要文件列表时管道不起作用。还有其他选择吗?

答案1

选项-l将使grep实用程序仅打印包含指定模式的文件的名称。我的系统手册对此选项的说明如下:

仅包含所选行的文件名称会写入标准输出。 grep 只会搜索文件,直到找到匹配项,从而使搜索成本可能更低。每个搜索的文件都会列出一次路径名。如果搜索标准输入,则写入字符串“(标准输入)”。

由于管道中的第二个grep是从标准输入读取,而不是从文件读取,因此除了数据到达其标准输入流之外,它不知道数据来自何处。这就是它返回文本字符串的原因(standard input)。这是距离比赛地点最接近的位置。

将第一个模式中的两个模式结合起来grep(其中知道它正在查找哪些文件),请参阅如何使用多个 AND 模式运行 grep?

答案2

使用“cut”删除“:”之后的字符串,然后您将获得文件部分(假设文件路径不包含冒号或换行符,并且与第二个模式本身不匹配)。

grep -ir "Some string" . |grep "Another string I want to find in the other grep's results" | cut -d ":" -f 1

如果出现重复,请使用“uniq”

grep -ir "string1" . | grep "string2" | cut -d: -f1 | uniq

答案3

(我假设您希望第二个grep匹配行的内容而不是文件名或两者,就像您的方法所做的那样)

POSIXly:

find . -type f -exec awk '
  FNR == 1 {found = 0}
  !found && tolower($0) ~ /some string/ && /other string/ {
    print FILENAME
    found = 1
    nextfile
  }' {} +

所关心的found是尚不支持的 awk 实现nextfilenextfile那么哪里是无操作)。如果您知道您的awk实现支持nextfile,您可以将其简化为:

 find . -type f -exec awk 'tolower($0) ~ /some string/ && /other string/ {
    print FILENAME; nextfile}' {} +

使用带有 PCRE 支持的 GNU grep,因为您希望一个匹配不区分大小写,而不是另一个:

grep -rlP '^(?=.*(?i:some string))(?=.*other string)' .

(?=...)是一个perl展望操作员。(?i:pattern)打开不区分大小写的匹配只为pattern。因此,这里我们在行的开头 ( ^) 进行匹配,只要它后面跟着任意数量的字符 ( .*) 后跟some string(不区分大小写),并且它(行的开头)后面跟着任意数量的字符和other string(区分大小写)。

如果您grep不支持-P,您可以使用该pcregrep命令(替换grep -rlPpcregrep -rl),或者如果模式不重叠,您可以这样做:

grep -rl -e '[sS][oO][mM][eE] [sS][tT][rR][iI][nN][gG].*other string' \
         -e 'other string.*[sS][oO][mM][eE] [sS][tT][rR][iI][nN][gG]' .

或者,如果您不关心两个匹配项不区分大小写:

grep -ril -e 'some string.*other string' \
          -e 'other string.*some string' .

答案4

这是所提供的所有解决方案中最短的解决方案。

find . -type f -exec perl -lne '
   /Some string/i and /other string/ and print($ARGV),close(*ARGV);
' {} +

grep -irZ "Some string" . |
perl -lsF'/\n/' -0ne '
   s/^/\n/ if $. == 1; s/$/\n/ if eof;

   $. == 1 and $prev = $F[1],next;
   push @{$h{$prev}}, $F[0];
   $prev = $F[1];

   END {
      grep($_ =~ /\Q${str2}/, @{$h{$_}}) and print for keys %h;
   }
' -- -str2="Another string"

工作原理:这里首先grep执行 a recursivecase-insensitive在当前目录中向下搜索“某个字符串”,并\0由于-Z给定的选项生成 null-separated( ) 记录grep

每条记录都包含文件名和匹配的行。唯一的问题是,由于 grep 没有\0在匹配行后面添加 a 的行为,因此排序不一致。为了解决这个限制,我们利用Perl读取以空分隔的记录并将这些记录拆分\n以将行与文件名分开。

因此,它们对可能涉及的文件名类型没有限制,但无论如何\0都是被禁止的。

相关内容