我试图给grep
一个模式文件(通过-f
),但我想知道哪些模式匹配搜索文件中的某些内容
例如,给定1.txt
:
a/(.*)
b/(.*)
b/c/(.*)
b/foo/(.*)
d/(.*)
e/(.*)
和2.txt
:
a/
a/foo/bar/
b/foo/
d/foo/
1.txt
中与2.txt
中的内容匹配的模式(省略(.*)
后缀)如下:
a/
b/
b/foo/
d/
我怎样才能找到匹配的模式列表"?
编辑:我只是在寻找前缀匹配,但我认为这个问题对于一般模式匹配非常有趣。
编辑:由于给出了一个基于for
循环的解决方案,我应该说我不是在考虑调用grep 10000次。 :)我已经拥有的工作解决方案(下面列出)非常慢:
for line in "${file1_arr[@]}"; do
if ! grep -qE "^$v(.*)\$"; then
echo "$line"
fi
done
理想情况下,我会以较少的开销寻找单个grep
电话。
答案 0 :(得分:2)
在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
修改:要输出每个正则表达式一次(如预期输出中显示的here),一旦使用了delete
,您就可以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/(.*)$
,这样就不会多次匹配和输出:
file1.txt
此外,我的测试显示大约60%的折扣(迷你笔记本电脑,1:16到29秒)GNU awk的修改时间(使用您在评论中提供的数据{{1 }和file2.txt
):
$ 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
通过使用直到第一个句点的字符串的开头作为哈希的键,加速减少了搜索空间,即:
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[$1 FS $2]
,但这只是一个快速测试。
答案 1 :(得分:1)
以下脚本:
#!/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
在1.txt
中打印与2.txt
匹配的行
a
b
b/foo
d
注释:
# 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"
答案 2 :(得分:0)
我没有看到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
解决方案有点难以阅读,导致了1.txt的布局,我想要b/foo/.*
这样的行。
以上命令将有2个错误:
当匹配位于线的一部分时,不匹配的部分将显示在输出中。这可以通过匹配垃圾
来修复# Use lines like 's#.*b/foo(.*)#b/foo#p'
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
编辑:
当您采用不同的方法时,不需要使用标记解决并恢复原始输入
在sed
中,您可以在不更改流的情况下将某些内容打印到标准输出
一种可能性(这种情况很慢)是使用
sed '/something/ eecho "something" '
另一种可能性是使用&#34; x&#34;命令(使用保持缓冲区交换模式空间)。你实际上想要一个sed
脚本,其中包含
\%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
如果不经常更改文件1.txt,您可能需要预处理该文件。
sed 's#([.][*])##; s#.*#\\%&% {h;s%.*%&%p;x} #' 1.txt > /tmp/sed.in
sed -nf /tmp/sed.in 2.txt | sort -u
答案 3 :(得分:0)
我尝试了基于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")
由于这不会产生任何额外的进程,只是在内存中,我怀疑这是非常有效的。我在詹姆斯答案下链接的文件的基准测试显示8-9秒。