我正在执行这样的嵌套 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 实现nextfile
(nextfile
那么哪里是无操作)。如果您知道您的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 -rlP
为pcregrep -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 recursive
,case-insensitive
在当前目录中向下搜索“某个字符串”,并\0
由于-Z
给定的选项生成 null-separated( ) 记录grep
。
每条记录都包含文件名和匹配的行。唯一的问题是,由于 grep 没有\0
在匹配行后面添加 a 的行为,因此排序不一致。为了解决这个限制,我们利用Perl
读取以空分隔的记录并将这些记录拆分\n
以将行与文件名分开。
因此,它们对可能涉及的文件名类型没有限制,但无论如何\0
都是被禁止的。