Bash 使用grep-f查找具有匹配项的模式本身
我试图给Bash 使用grep-f查找具有匹配项的模式本身,bash,grep,Bash,Grep,我试图给grep一个模式文件(通过-f),但我想知道哪些模式与搜索文件中的内容相匹配 例如,给定1.txt: a/(.*) b/(.*) b/c/(.*) b/foo/(.*) d/(.*) e/(.*) a/ a/foo/bar/ b/foo/ d/foo/ a b
grep
一个模式文件(通过-f
),但我想知道哪些模式与搜索文件中的内容相匹配
例如,给定1.txt
:
a/(.*)
b/(.*)
b/c/(.*)
b/foo/(.*)
d/(.*)
e/(.*)
a/
a/foo/bar/
b/foo/
d/foo/
a
b
b/foo
d
$ awk '
BEGIN {
FS="." # . splits the url
}
NR==FNR { a[$1][$0]; next } # we index on the first part of url
{
for(i in a[$1]) # search space decreased
if($0 ~ i) {
print i
delete a[$1][i]
}
}' file1.txt file2.txt
和2.txt
:
a/(.*)
b/(.*)
b/c/(.*)
b/foo/(.*)
d/(.*)
e/(.*)
a/
a/foo/bar/
b/foo/
d/foo/
a
b
b/foo
d
$ awk '
BEGIN {
FS="." # . splits the url
}
NR==FNR { a[$1][$0]; next } # we index on the first part of url
{
for(i in a[$1]) # search space decreased
if($0 ~ i) {
print i
delete a[$1][i]
}
}' file1.txt file2.txt
1.txt
中与2.txt
中的内容匹配的模式如下(省略(.*)
后缀):
a/
b/
b/foo/
d/
如何“找到匹配的模式列表”
编辑:我只是在寻找前缀匹配,但我认为这个问题对于一般的模式匹配来说已经足够有趣了
EDIT:既然给出了基于
循环的解决方案,我应该说我不打算调用grep 10000次我已经拥有的工作解决方案(如下所列)相当缓慢:
for line in "${file1_arr[@]}"; do
if ! grep -qE "^$v(.*)\$"; then
echo "$line"
fi
done
理想情况下,我正在寻找一个开销较小的grep
呼叫。以下脚本:
#!/usr/bin/env bash
lines=$(wc -l < 1.txt)
for (( i=1; i<=$lines; i++ )); do
line=$(sed -n "$i"p 1.txt)
line=$(sed "s/\/(.*)$//" <<< "$line")
grep -E "$line" 2.txt 1>/dev/null && echo "$line"
done
评论:
# gets a single line from 1.txt
line=$(sed -n "$i"p 1.txt)
# removes trailing pattern /(.*) from $line variable
line=$(sed "s/\/(.*)$//" <<< "$line")
# if $line matches in 2.txt, print $line
grep -E "$line" 2.txt 1>/dev/null && echo "$line"
#从1.txt获取一行
行=$(sed-n“$i”p1.txt)
#从$line变量中删除尾随模式/(*)
行=$(sed“s/\/(.*)$/”在awk中:
$ awk 'NR==FNR{a[$0]=FNR;next}{for(i in a)if($0 ~ i)print i,$0}' 1.txt 2.txt
a/(.*) a/
a/(.*) a/foo/bar
b/(.*) b/foo
d/(.*) d/foo
解释:
$ awk ' # yes
NR==FNR { # process first file
a[$0]=FNR # hash regex, store record number just in case
next # process next record
}
{ # process second file
for(i in a) # loop every entry in 1.txt
if($0 ~ i) # if regex matches record
print i,$0} # print all matching regex and record
' 1.txt 2.txt
编辑:要仅输出每个正则表达式一次(如预期输出中所示),您可以从a
中删除该正则表达式,一旦使用,这样它就不会被匹配和输出多次:
$ awk '
NR==FNR { a[$0]; next }
{
for(i in a)
if($0 ~ i) {
print i
delete a[i] # deleted regex wont get matched again
}
}' 1.txt 2.txt
vendor/cloud.google.com/go/compute/metadata/(.*)$
vendor/cloud.google.com/go/compute/(.*)$
vendor/cloud.google.com/go/(.*)$
vendor/cloud.google.com/(.*)$
vendor/github.com/Azure/azure-sdk-for-go/arm/dns/(.*)$
vendor/github.com/Azure/azure-sdk-for-go/arm/(.*)$
vendor/github.com/Azure/azure-sdk-for-go/(.*)$
vendor/github.com/Azure/(.*)$
vendor/github.com/(.*)$
另外,我的测试显示GNU awk修改的时间(使用您在评论中提供的数据,file1.txt
和file2.txt
)大约减少了60%(迷你笔记本电脑,1:16到29秒):
加速通过使用字符串的开头(直到第一个句点)作为哈希的键来减少搜索空间,即:
FS="." # split at first .
...
a[vendor/github][vendor/github.com/Azure/(.*)$] # example of a hash
...
for(i in a[$1]) # search space decreased
现在它不必在整个哈希中搜索匹配的正则表达式。更可行的方法可能是使用FS=“/”a[$1fs$2]
,但这只是一个快速测试。我没有看到使用grep
的解决方案,但是sed
是awk
的替代方案。
使用sed
我希望在1.txt中看到类似b/foo/*
的模式,但我将展示基于(.*)
的解决方案
第一个命令的目的是构造sed
构造,当输入行与正则表达式匹配时,它将用正则表达式替换输入行
sed -rn 's#b/c/(.*)#b/c/#p' 2.txt
这可以通过
# Use subprocess
sed 's/\(.*\)\(([.][*])\)/s#\1\2#\1#p/' 1.txt
# resulting in
sed -rnf <(sed 's/\(.*\)\(([.][*])\)/s#\1\2#\1#p/' 1.txt) 2.txt| sort -u
第二个错误是2.txt
中有两个匹配项的字符串将只匹配一次(第一个匹配项将编辑流中的行)。
这可以通过为匹配行添加一些唯一标记(我将使用\a
)并在输出上重复输入行(使用\n&
)来解决。
可通过查找\a
标记查看输出
sed -rnf <(sed 's/\(.*\)\(([.][*])\)/s#.*\1\2#\\a\1\\n\&#p/' 1.txt) 2.txt|
sed -rn '/\a/ s/.(.*)/\1/p' | sort -u
另一种可能是使用“x”命令(该命令与保持缓冲区交换模式空间)
\%a/% {h;s%.*%a/%p;x}
\%b/% {h;s%.*%b/%p;x}
\%b/c/% {h;s%.*%b/c/%p;x}
\%b/foo/% {h;s%.*%b/foo/%p;x}
\%d/% {h;s%.*%d/%p;x}
\%e/% {h;s%.*%e/%p;x}
使用上述方法,sed
解决方案简化为
sed -nf <(
sed 's#([.][*])##; s#.*#\\%&% {h;s%.*%&%p;x} #' 1.txt
) 2.txt | sort -u
我尝试了基于awk
和sed
的解决方案,我意识到如果我在内存中读取这两个文件,使用bash的内置regexp引擎可以更快地完成这项工作
基本上就是这样
text="$(cat 2.txt)" # read 2.txt
while read -r line; do # for each 'line' from 1.txt
re=[^\b]*${line} # prepend ^ or \b to the pattern
if [[ "$text" =~ $re ]]; then # match the pattern to 2.txt
echo "${line}" # if there's a match, print the pattern
fi
done < <(cat "1.txt")
text=“$(cat 2.txt)”#读取2.txt
读取时-r行;对1.txt中的每个“行”执行
re=[^\b]*${line}#在模式前添加^or\b
如果[[“$text”=~$re]],则#将模式与2.txt匹配
echo“${line}”#如果有匹配项,则打印模式
fi
完成<我已经有了<代码>for
循环。问题是它太慢了,因为在我的情况下,它调用grep>10000次,这会产生进程开销。我理想地寻找一个grep命令。我已经有了解决方案:<代码>for line in“${values[@]}”;do;if!grep-qE”^$v(.*\$”,然后回显“$line”;fi;done
您可以包括-m1
选项来加速grep。但这并不能解决主要问题,即对grep的多次调用。您的所有模式是否都由固定字符串加上(.*)
?(顺便说一句,我不认为您的“有效解决方案”将(.*)$
放在所有模式的末尾只会减慢匹配速度。这对匹配的内容没有影响。如果有多个模式与给定行匹配,您希望得到什么输出?您确定b/foo/(.*)吗
from 1.txt应该匹配b/foo
from 2.txt?最后,请您指定您将使用的模式类型以及大致有多少种模式。您的示例表明,它们都是没有正则表达式运算符的简单前缀匹配;如果是这样,有一些非常有效的解决方案。回答非常好。IMHO awk解决方案ons非常适合处理繁重的模式匹配问题。@rici:请解释一下,您的评论太短了:)@rann基准可以比较使用原始解决方案、优秀的awk
one和我的sed
解决方案所需的时间。也许还要补充一点,awk
不仅性能很好,而且比充满反斜杠的代码更易于阅读/维护,并且可以更好地处理具有特殊字符的代码(在我的解决方案中,我真的希望输入文件没有\code>或\a
字符)。如果真的有10000个模式,正如OP所建议的,awk可能仍然比grep快,但这并不确定,这就是我建议基准测试的原因。理想的做法是并行检查所有模式,如果模式仅限于(数学)正则表达式,或者更好地限于固定字符串(如示例中所示),这将是直接的