Я хочу отсортировать список доменных имен (белый список веб-фильтров), начиная с 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
) нет, пробовал.