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快,但这并不确定,这就是我建议基准测试的原因。理想的做法是并行检查所有模式,如果模式仅限于(数学)正则表达式,或者更好地限于固定字符串(如示例中所示),这将是直接的