Назад | Перейти на главную страницу

Отсортировать список доменных имен (FQDN), начиная с tld и работая слева

Я хочу отсортировать список доменных имен (белый список веб-фильтров), начиная с TLD и двигаясь вверх. Я ищу любые инструменты * nix или Windows, которые могут сделать это легко, хотя сценарий тоже подойдет.

Итак, если это список, который вам дан

www.activityvillage.co.uk 
ajax.googleapis.com 
akhet.co.uk 
alchemy.l8r.pl 
au.af.mil 
bbc.co.uk 
bensguide.gpo.gov 
chrome.angrybirds.com 
cms.hss.gov 
crl.godaddy.com 
digitalhistory.uh.edu 
digital.library.okstate.edu 
digital.olivesoftware.com

Это то, что я хочу получить.

chrome.angrybirds.com 
crl.godaddy.com 
ajax.googleapis.com 
digital.olivesoftware.com 
digital.library.okstate.edu 
digitalhistory.uh.edu 
bensguide.gpo.gov 
cms.hss.gov 
au.af.mil 
alchemy.l8r.pl 
www.activityvillage.co.uk 
akhet.co.uk 
bbc.co.uk

На всякий случай, если вам интересно, почему у Squidguard есть ошибка / недостаток дизайна. Если оба www.example.com и example.com оба включены в список, то example.com запись игнорируется, и вы можете посещать контент только из www.example.com. У меня есть несколько больших списков, которые нужно очистить, потому что кто-то добавил записи, не глядя.

Этот простой скрипт Python сделает то, что вы хотите. В этом примере я называю файл domain-sort.py:

#!/usr/bin/env python
from fileinput import input
for y in sorted([x.strip().split('.')[::-1] for x in input()]): print '.'.join(y[::-1])

Для его запуска используйте:

cat file.txt | ./domain-sort.py

Обратите внимание, что это выглядит немного уродливее, поскольку я написал это как более или менее простой однострочник, который мне пришлось использовать срезное обозначение [::-1] где отрицательные значения работают, чтобы сделать копию того же списка в обратном порядке вместо использования более декларативного reverse() который делает это на месте таким образом, чтобы нарушить возможность комбинирования.

А вот немного более длинная, но, возможно, более читаемая версия, в которой используется reversed() который возвращает итератор, поэтому необходимо также обернуть его в list() чтобы использовать итератор и создать список:

#!/usr/bin/env python
from fileinput import input
for y in sorted([list(reversed(x.strip().split('.'))) for x in input()]): print '.'.join(list(reversed(y)))

Для файла с 1500 произвольно отсортированными строками это занимает ~ 0,02 секунды:

Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.02
Maximum resident set size (kbytes): 21632

Для файла со 150 000 случайно отсортированными строками это занимает чуть более 3 секунд:

Elapsed (wall clock) time (h:mm:ss or m:ss): 0:03.20
Maximum resident set size (kbytes): 180128

Вот, возможно, более читаемая версия, которая делает reverse() и sort() на месте, но он выполняется за то же время и фактически занимает немного больше памяти.

#!/usr/bin/env python
from fileinput import input

data = []
for x in input():
   d = x.strip().split('.')
   d.reverse()
   data.append(d)
data.sort()
for y in data:
   y.reverse()
   print '.'.join(y)

Для файла с 1500 произвольно отсортированными строками это занимает ~ 0,02 секунды:

Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.02
Maximum resident set size (kbytes): 22096

Для файла со 150 000 случайно отсортированными строками это занимает чуть более 3 секунд:

Elapsed (wall clock) time (h:mm:ss or m:ss): 0:03.08
Maximum resident set size (kbytes): 219152

Вот сценарий PowerShell, который должен делать то, что вы хотите. По сути, он помещает все TLD в массив, меняет местами каждый TLD, сортирует его, возвращает в исходный порядок, а затем сохраняет в другой файл.

$TLDs = Get-Content .\TLDsToSort-In.txt
$TLDStrings = @();

foreach ($TLD in $TLDs){
    $split = $TLD.split(".")
    [array]::Reverse($split)
    $TLDStrings += ,$split
}

$TLDStrings = $TLDStrings|Sort-Object

foreach ($TLD in $TLDStrings){[array]::Reverse($TLD)}

$TLDStrings | %{[string]::join('.', $_)} | Out-File .\TLDsToSort-Out.txt

Прогнал 1500 записей - 5 секунд на достаточно мощном настольном компьютере.

cat domain.txt | rev | сортировать | rev

Немного менее загадочный или, по крайней мере, более красивый Perl:

use warnings;
use strict;

my @lines = <>;
chomp @lines;

@lines =
    map { join ".", reverse split /\./ }
    sort
    map { join ".", reverse split /\./ }
    @lines;

print "$_\n" for @lines;

Это простой пример Преобразование Гуттмана – Рослера: мы преобразуем строки в соответствующую сортируемую форму (здесь разделяем доменное имя на точки и меняем порядок частей), сортируем их, используя собственную лексикографическую сортировку, а затем преобразуем строки обратно в исходную форму.

В сценариях Unix: обратное, сортировка и обратное:

awk -F "." '{for(i=NF; i > 1; i--) printf "%s.", $i; print $1}' file |
  sort |
  awk -F "." '{for(i=NF; i > 1; i--) printf "%s.", $i; print $1}'

Вот он (короткий и загадочный) perl:

#!/usr/bin/perl -w
@d = <>; chomp @d;
for (@d) { $rd{$_} = [ reverse split /\./ ] }
for $d (sort { for $i (0..$#{$rd{$a}}) {
        $i > $#{$rd{$b}} and return 1;
        $rd{$a}[$i] cmp $rd{$b}[$i] or next;
        return $rd{$a}[$i] cmp $rd{$b}[$i];
} } @d) { print "$d\n" }
awk -F"." 's="";{for(i=NF;i>0;i--) {if (i<NF) s=s "." $i; else s=$i}; print s}' <<<filename>>> | sort | awk -F"." 's="";{for(i=NF;i>0;i--) {if (i<NF) s=s "." $i; else s=$i}; print s}'

Что это делает, так это отменить каждое поле в доменном имени, отсортировать и отменить обратно.

Это действительно сортирует список доменов, лексикографически основанный на каждой части доменного имени, справа налево.

Обратное решение (rev <<<filename>>> | sort | rev) нет, пробовал.