Bash-如果主域位于文件A中(或通过管道传输),则从文件B中删除域/子域

时间:2018-08-18 11:23:12

标签: bash awk grep

我希望能够使用管道输入的域参考文件或参考文件(文件B )从文件A

例如,我不能使用grep "bbc.co.uk",因为它将包括诸如cbbc.co.uk之类的条目。

我尝试使用while读取循环来遍历文件B ,运行grep -E "^([^.\s]+\.)*${escaped_domain}$" fileA来识别域和子域,但这非常慢,因为需要进行比较

有更好的方法吗?也许使用awk?

文件B(或管道输入)

〜3万行

bbc.co.uk
amazon.co.uk
doubleclick.net

文件A

〜150k +线

123123.test.bbc.co.uk
123434.rwr.amazon.co.uk
ads.bbc.co.uk
adsa.23432.doubleclick.net
amazon.co.uk
bbc.co.uk
cbbc.co.uk
damazon.co.uk
fsdfsfs.doubleclick.net
test.amazon.co.uk
test.bbc.co.uk
test.damazon.co.uk

所需的输出:

cbbc.co.uk
damazon.co.uk
test.damazon.co.uk

当前方法(与grep / regexps不同的输入)

# Convert input: address=/test.com/ -> ^([^.\s]+\.)*test\.com$
regexList=$(cat fileB | 
    sed 's/\./\\./g' |
    awk -F '/' {'print "^([^.\s]+\.)*"$2"$"'})

while read -r regex; do
    grep -E $regex filaA
done <<< "$regexList"

2 个答案:

答案 0 :(得分:2)

$ awk '
    NR==FNR {
        gsub(/[^^]/,"[&]")
        gsub(/\^/,"\\^")
        doms["(^|[.])"$0"$"]
        next
    }
    {
        for (dom in doms) {
            if ($0 ~ dom) {
                next
            }
        }
        print
    }
' fileB fileA
cbbc.co.uk
damazon.co.uk
test.damazon.co.uk

或通过管道:

$ cat fileB | awk '...' - fileA

如果fileB足够小,则不需要数组,您只需构建并测试所有域的1个正则表达式即可:

$ awk '
    NR==1 { doms = "(^|[.])(" $0; next }
    NR==FNR {
        gsub(/[^^]/,"[&]")
        gsub(/\^/,"\\^")
        doms = doms "|" $0
        next
    }
    FNR==1 { doms = doms ")$" }
    $0 !~ doms
' fileB fileA
cbbc.co.uk
damazon.co.uk
test.damazon.co.uk

每个脚本中的两个gsub()确保将域中的所有正则表达式元字符都视为文字字符。有关其工作原理和方式的详细信息,请参见is-it-possible-to-escape-regex-metacharacters-reliably-with-sed

答案 1 :(得分:1)

您可以将第一个文件转换为一组要删除的正则表达式:

sed 's/[][\\.^$*+?()]/\\&/g;s/.*/^([^.]+\\.)*&$/' fileB

输出是可以传递给grep -vE的正则表达式序列:

... | grep -vEf - fileA

grep -Ef一次可以保留多少内存是有限制的,但是30k表达式可能在现代硬件的限制之内。在最坏的情况下,将fileA分成两半并运行两次。