2013-09-04 12 views
5

Próbuję numerycznie sortować serię plików wyjściowych za pomocą polecenia ls, które pasuje do wzorca ABCDE1234A1789.RST.txt lub ABCDE12345A1789.RST.txt przez pole "789".Używanie polecenia bash sort w nazwach plików o zmiennej długości

W powyższych wzorach przykładowych, ABCDE jest taki sam dla wszystkich plików, 1234 lub 12345 są cyframi, które są różne, ale zawsze mają 4 lub 5 cyfr. A1 ma taką samą długość dla wszystkich plików, ale wartość może się różnić, więc niestety nie można jej użyć jako ogranicznika. Wszystko po pierwszym . jest takie samo dla wszystkich plików. Coś jak:

ls -l *.RST.txt | sort -k +9.13 | awk '{print $9} ' > file-list.txt 

dopasuje krótsze nazwy plików, ale nie z powodu dłuższych zmiennej długości znaków przed polem chcę sortować.

Czy istnieje sposób na sortowanie wszystkich plików bez wcześniejszego dopełniania krótszych plików, aby miały taką samą długość

+1

FYI - polecenie 'sort' nie jest częścią bash, ale standardowe narzędzie UNIX. Jako taki jest dostępny dla dowolnego programu (i dowolnej powłoki). –

Odpowiedz

4

Perl na ratunek!

perl -e 'print "$_\n" for sort { substr($a, -11, 3) cmp substr($b, -11, 3) } glob "*.RST.txt"' 

Jeżeli perl jest nowsza (5.10 lub nowsza), można skrócić do

perl -E 'say for sort { substr($a, -11, 3) cmp substr($b, -11, 3) } glob "*.RST.txt"' 
+0

Dziękuję za chorobę (i dziękuję, perl). Używając wersji pre-5.10, więc najlepsza linia działa idealnie. –

2

konwencjonalny sposób, aby to zrobić w bash jest wyodrębnić pola sortowania. Wyjątkiem polecenia sortowania dodaje realizowany jest w czystej bash sam:

sort_names_by_first_num() { 
    shopt -s extglob 
    for f; do 
    first_num="${f##+([^0-9])}"; 
    first_num=${first_num%[^0-9]*}; 
    [[ $first_num ]] && printf '%s\t%s\n' "$first_num" "$f" 
    done | sort -n | while IFS='' read -r name; do name=${name#*$'\t'}; printf '%s\n' "$name"; done 
} 

sort_names_by_first_num *.RST.txt 

Powiedział, nowej linii ograniczającej nazw (jak wydaje się zadzwonić na to pytanie) jest złą praktyką: Nazwy plików na systemach plików UNIX są dopuszczone do zawierają nowe wiersze w swoich nazwach, więc rozdzielenie ich przez nowe wiersze na liście oznacza, że ​​twoja lista nie może zawierać istotnego podzbioru zakresu poprawnych nazw. Lepszą praktyką jest NUL-ograniczanie swoich list. Robi to będzie wyglądać tak:

sort_names_by_first_num() { 
    shopt -s extglob 
    for f; do 
    first_num="${f##+([^0-9])}"; 
    first_num=${first_num%[^0-9]*}; 
    [[ $first_num ]] && printf '%s\t%s\0' "$first_num" "$f" 
    done | sort -n -z | while IFS='' read -r -d '' name; do name=${name#*$'\t'}; printf '%s\0' "$name"; done 
} 

sort_names_by_first_num *.RST.txt 
+0

Dzięki, Charles, za bardzo wszechstronną alternatywę. W tym przypadku, perl jest dostępny dla użytkowników i jest prawdopodobnie nieco łatwiejszy w implementacji niż funkcja bash, ale bardzo doceniam tę opcję! –

3

Ponieważ z części nazwy pliku, który już zidentyfikowane jako niezmienny, rzeczywiście można zbudować klucz, który będzie używany rodzaj:

$ echo ABCDE{99999,8765,9876,345,654,23,21,2,3}A1789.RST.txt \ 
    | fmt -w1 \ 
    | sort -tE -k2,2n --debug 
ABCDE2A1789.RST.txt 
    _ 
___________________ 
ABCDE3A1789.RST.txt 
    _ 
___________________ 
ABCDE21A1789.RST.txt 
    __ 
etc. 

co to to sortuj, aby oddzielić pola na znaku E, a następnie użyj drugiego pola numerycznie. --debug dotarł do coreutils 8.6 i może być bardzo pomocny w dokładnym sprawdzeniu, co robi.

Powiązane problemy