Сортировать список доменных имен (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, У меня есть несколько больших списков, которые нуждаются в некоторой очистке, потому что кто-то добавил записи без предварительного просмотра.

10 ответов

Решение

Этот простой скрипт на 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

Для файла с 150000 случайно отсортированных строк это занимает чуть более 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

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

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

Кошка domain.txt | рев | сортировать | оборот

Вот сценарий 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 секунд на достаточно мощном рабочем столе.

В сценариях 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:

use warnings;
use strict;

my @lines = <>;
chomp @lines;

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

print "$_\n" for @lines;

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

Вот он (короткий и загадочный) 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" }

Хорошо, я добавлю еще одно решение (через 10 лет), потому что в других решениях Python не упоминалась отсортированная клавишная функция, которая читабельна и проста для понимания:

      #!/usr/bin/env python
import sys
def kf(v):
    """Key-function to sort domains in tld order"""
    return '.'.join(reversed(v.strip().split('.')))

sys.stdout.writelines(sorted(sys.stdin, key=kf))

Еще один ответ: использование встроенной сортировки awk для лексической сортировки каждой части имени:

      awk -F. '{k="";for(x=NF;x>0;x--){k=k"."$x};o[k]=$0};END{PROCINFO["sorted_in"]="@ind_str_asc";for(n in o){print o[n]}}' < infile > outfile

Более читабельно:

      awk -F. '
  {
    k=""
    for(x=NF;x>0;x--){k=k"."$x}
    o[k]=$0
  }

  END{
    PROCINFO["sorted_in"]="@ind_str_asc"
    for(n in o){
      print o[n]
    }
  }
' < infile > outfile

В моей системе awk на самом деле является GNU Awk 5.1.0; и неясно, какие версии awk/mawk/nawk/gawk/etc поддерживают PROCINFO["sorted_in"]. Однако первый awk, который я когда-либо использовал, сортировал вывод в алфавитном порядке по ключу массива, поэтому PROCINFO на самом деле предназначен только для того, чтобы «современные» версии gawk не испортили вывод. :-)

В JavaScript:

      function compareName(a, b) {
    const aReverse = a.split('.').reverse().join('.')
    const bReverse = b.split('.').reverse().join('.')
    return aReverse.toLowerCase().localeCompare(bReverse.toLowerCase())
}

const myNames = ['a.example', 'Z.a.example', 'yl.a.example', 'example']
myNames.sort(compareName)

Использует тот же алгоритм, что и ответ @aculich .

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) нет, я пробовал.

Другие вопросы по тегам