删除子域或现有域

删除子域或现有域

我有一个域列表,示例是:

account.google.com
drive.google.com
google.com
bgoogle.com
yahoo.co.uk
stats.wikipedia.org
media.wikipedia.org
files.media.wikipedia.org
bible.com

我想删除现有域的所有子域。例如,对于上面的列表,由于google.commedia.wikipedia.org具有子域,因此应删除这些子域。

所以上面的列表应该导致:

google.com
bgoogle.com
yahoo.co.uk
stats.wikipedia.org
media.wikipedia.org
bible.com

我尝试用Python编写一些代码,但是需要很长时间才能完成。我怎样才能以最快的方式执行这个任务?

答案1

听起来你的问题可以重新表述为“只保留最长的非.后跟 a ,直到行尾为止.不保留其他;使这些唯一”。.如果是这样,您可以执行以下操作:

$ awk -F"." -v OFS="." '{print $(NF-1),$(NF)}' file | sort -u
bgoogle.com
bible.com
google.com
wikipedia.org

或者,完成整个事情awk

$ awk -F"." '{ k[$(NF-1)"."$(NF)]++}END{for (i in k){print i}}' file 
bgoogle.com
bible.com
wikipedia.org
google.com

或者 GNU grep

$ grep -oP '[^.]+\.[^.]+$' file  | sort -u
bgoogle.com
bible.com
google.com
wikipedia.org

或者 perl 和 GNU 排序

$ perl -pe 's/.*?([^.]+\.[^.]+$)/$1/' file | sort -u
bgoogle.com
bible.com
google.com
wikipedia.org

或者只是 perl

$ perl -ne '$k{$1}++ for s/.*?([^.]+\.[^.]+$)/$1/; END{ print keys(%k) }' file 
bible.com
bgoogle.com
wikipedia.org
google.com

或者只是 perl

$ perl -ne 's/.*?([^.]+\.[^.]+)$/$1/; next if ++$k{$1}>1; print' file 
google.com
bgoogle.com
wikipedia.org
bible.com

或者 sed 并排序

$ sed -E 's/.*\.([^.]+\.[^.]+)$/\1/' file | sort -u
bgoogle.com
bible.com
google.com
wikipedia.org

答案2

$ sed -e 's/\./\\./g' -e 's,.*,/\\.&$/d,' file | sed -f /dev/stdin file
google.com
bgoogle.com
yahoo.co.uk
stats.wikipedia.org
media.wikipedia.org
bible.com

这首先用于从输入文件sed创建编辑脚本。sedsed命令首先转义当前行上的每个点,然后将修改后的行转换为d具有特定正则表达式触发器的命令。

对于问题中提供的文件,该编辑脚本将如下所示:

/\.account\.google\.com$/d
/\.drive\.google\.com$/d
/\.google\.com$/d
/\.bgoogle\.com$/d
/\.yahoo\.co\.uk$/d
/\.stats\.wikipedia\.org$/d
/\.media\.wikipedia\.org$/d
/\.files\.media\.wikipedia\.org$/d
/\.bible\.com$/d

此脚本将删除包含以 结尾的主机名的任何输入行,.后跟原始数据中的任何主机名。例如,这将删除数据中的aaa.bbb.cccif ,因为它与正则表达式 匹配。bbb.cccaaa.bbb.ccc\.bbb\.ccc$

然后,通过第二次调用将该脚本应用于原始数据sed,通过其标准输入流读取编辑脚本。


通过对初始命令进行一些小的修改,我们可以用以下sed命令进行消除:grepsed

$ sed -e 's/\./\\./g' -e 's/.*/\\.&$/' file | grep -v -f /dev/stdin file
google.com
bgoogle.com
yahoo.co.uk
stats.wikipedia.org
media.wikipedia.org
bible.com

这将创建一组裸露的正则表达式以供grep读取-f

\.account\.google\.com$
\.drive\.google\.com$
\.google\.com$
\.bgoogle\.com$
\.yahoo\.co\.uk$
\.stats\.wikipedia\.org$
\.media\.wikipedia\.org$
\.files\.media\.wikipedia\.org$
\.bible\.com$

grep实用程序用于-v从输入中删除任何匹配的行。

答案3

使用(以前称为 Perl_6)

...翻译@terdon 优秀的 Perl 解决方案:

~$ raku -ne 'BEGIN my %k; %k{$0}++ for s/ .*? (<-[.]>+ \. <-[.]>+ $)/$0/; END .key.put for %k;'  file

#OR:

~$ raku -ne 'BEGIN my %k; s/ .*? (<-[.]>+ \. <-[.]>+ $) /$0/; next if ++%k{$0} > 1; .put;'  file

以下是用 Raku(Perl 编程语言家族的成员)编写的解决方案。 Raku 具有不变的“sigils”以及许多不错的默认设置,例如扩展正则表达式的能力(/xPerl 中的 -option)。自定义字符类适用<+[…]>于正字符类和<-[…]>负字符类。 Raku 中的捕获从 开始,与$0Perl 不同,Perl 从 开始$1

输入示例:

account.google.com
drive.google.com
google.com
bgoogle.com
stats.wikipedia.org
media.wikipedia.org
files.media.wikipedia.org
bible.com

示例输出:

google.com
bgoogle.com
wikipedia.org
bible.com

https://raku.org

答案4

这可能是您想要使用任何 awk 执行的操作:

$ cat tst.sh
#!/usr/bin/env bash

awk '
    { doms[$0] }
    END {
        for ( dom in doms ) {
            n = gsub(/[.]/,"&",dom)
            parent = dom
            foundParent = 0
            for ( i=1; i<n; i++ ) {
                sub(/[^.]+\./,"",parent)
                if ( parent in doms ) {
                    foundParent = 1
                    break
                }
            }
            if ( !foundParent ) {
                print dom
            }
        }
    }
' "${@:--}"

$ ./tst.sh file
media.wikipedia.org
bgoogle.com
bible.com
google.com
stats.wikipedia.org

或者,如果您不想将整个文件存储在 awk 的内存中:

$ cat tst.sh
#!/usr/bin/env bash

awk '
    BEGIN { FS=OFS="." }
    {
        for ( i=NF; i>=1; i-- ) {
            printf "%s%s", $i, (i>1 ? OFS : ORS)
        }
    }
' "${@:--}" |
sort -u |
awk '
    BEGIN { FS=OFS="." }
    index($0,base".") != 1 {
        base = $0
        for ( i=NF; i>=1; i-- ) {
            printf "%s%s", $i, (i>1 ? OFS : ORS)
        }
    }

'

$ ./tst.sh file
bgoogle.com
bible.com
google.com
media.wikipedia.org
stats.wikipedia.org

上面的第一个脚本将所有域读取到数组中的内存中doms[],然后循环遍历它们,将“.”之间的前导字符串剥离到只剩下 2 个的位置(因此files.media.wikipedia.org变为media.wikipedia.org和 then fwikipedia.org),同时检查该父字符串是否也存在存在于doms[]数组中,如果存在,那么我们知道当前域是该父域的子域,该父域也存在于输入中,因此我们不打印它。

第二个脚本使用 awk 反向打印每个域名,因此files.media.wikipedia.org打印为org.wikipedia.media.files,然后将整个列表通过管道传输到该列表,该列表sort -u将丢弃重复项(可选),结果是较短的(即父)域名将立即打印在前面他们的子域。然后接下来的awk只是检查当前反向域名是否是前一个域名开头的子字符串,而该域名本身不是子域 - 如果是,则该域是子域,所以跳过它,如果不是,则打印它(重新反向它恢复到原来的顺序)并记住它作为下一个要比较的域的新基本父级。如果不清楚,请单独运行每个命令以查看它们的输出内容。

相关内容