Sortuj pliki tekstowe z wieloma wierszami jako wiersz

14

Mam plik tekstowy w tym formacie:

####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Chcę posortować ten plik według KEYlinii i zachować w nim kolejne 4 wiersze, więc posortowany wynik powinien wyglądać następująco:

####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

czy jest na to sposób?

RYN
źródło
5
NIE poprzeczny słupek proszę
Zanna
@Zanna: Wydaje mi się, że wykluczone są sekcje unix i askubuntu, ponieważ te dwie mają ze sobą wiele pokrywających się! Myślę, że przeczytałem o tym w sekcji meta
unixa
2
odpowiednie meta pytanie zadane tutaj przez AU mod :) W jaki sposób należy obsłużyć pytania zamieszczone w części Zadaj Ubuntu?
Zanna
@RYN Problemem nie byłoby nakładanie się, w rzeczywistości wiele witryn SE nakłada się, ale osoby udzielające odpowiedzi mogą nie wiedzieć o odpowiedziach na drugiej stronie.
phk

Odpowiedzi:

13

msort(1)został zaprojektowany, aby móc sortować pliki z rekordami wieloliniowymi. Ma opcjonalne GUI, a także normalną i użyteczną dla ludzi wersję wiersza poleceń. (Przynajmniej ludzie, którzy lubią uważnie czytać podręczniki i szukają przykładów ...)

AFAICT, nie możesz użyć dowolnego wzorca dla rekordów, więc chyba że twoje rekordy mają stały rozmiar (w bajtach, a nie znakach lub wierszach). msortma -bopcję dla rekordów, które są blokami linii oddzielonymi pustymi liniami.

Możesz przekształcić swoje dane wejściowe w format, który będzie działał -bdość łatwo, umieszczając pusty wiersz przed każdym ###...(oprócz pierwszego).

Domyślnie drukuje statystyki na stderr, więc przynajmniej łatwo jest stwierdzić, kiedy nie posortował, ponieważ uważał, że całe wejście było pojedynczym rekordem.


msortdziała na twoich danych. sedPolecenie poprzedza nowego wiersza do każdej #+linii z wyjątkiem linii 1. -wsortuje cały rekord (leksykograficznie). Istnieją opcje wybierania, której części rekordu użyć jako klucza, ale nie potrzebowałem ich.

Pominąłem również usuwanie nowych linii.

$ sed '2,$ s/^#\+/\n&/' unsorted.records | msort -b -w 2>/dev/null 
####################################
KEY1
VAL11
VAL12
VAL13
VAL14

####################################
KEY2
VAL21
VAL22
VAL23
VAL24

####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Nie miałem szczęścia -r '#'użyć tego jako separatora rekordów. Myślał, że cały plik to jedna płyta.

Peter Cordes
źródło
Dziękuję Ci bardzo; msortjest bardzo przydatny; dzięki ( -rwydaje się, że jest tak, ponieważ użyłem więcej niż jednego # i użyłem -di zadziałało
RYN
chłodny! (+1) msort -qwr '#' ex działa dla mnie (cóż, wydziela separator
zapisu
9

Rozwiązaniem jest najpierw zmiana wysuwu wiersza w bloku na wybrany nieużywany znak („|” w poniższym przykładzie), posortowanie wyniku i powrót wybranego separatora do oryginalnego wysuwu wiersza:

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'
Xhienne
źródło
1
Dzięki; to działa, ale jest bardzo brudne, szczególnie gdy dane są brudne! jeśli wiersze po kluczu miały wartość 100, muszę ;Ntam wstawić 100 , a znalezienie znaku, który nie jest używany w samym tekście, może być trudne; bardzo dobrze nadaje się do ... sortlub awksortowania wielowierszowego
RYN
5
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 slurps cały plik
  • /(....)/g dopasuj i wyodrębnij rekordy
  • print sort ... posortuj je i wydrukuj
JJoao
źródło
2

Oto inny sposób, który powinien działać z dowolną liczbą linii w KEYsekcji:

# extract delimiter
delim=$(head -n1 <infile)
sed '/#/d;/KEY/h;G;s/\n/\x02/' infile | nl -ba -nrz -s $'\002' | sort -t $'\002' -k3 -k1,1 |
cut -d $'\002' -f2 | sed '/KEY/{x;s/.*/'"${delim}"'/;G}'

Działa to poprzez zapisanie separatora w zmiennej (a następnie usunięcie go z wejścia). Następnie dodaje KEY*znak do każdej linii w odpowiedniej sekcji, używając niskiego znaku ascii (co prawdopodobnie nie npojawi się na wejściu) jako separatora, a następnie analizuje wszystkie lines przy użyciu tego samego separatora. Jest to tylko kwestia wejścia sortprzez 3. i 1. pole i cutwybrania środkowej kolumny, a następnie przywrócenia ograniczników przez finał sed. Zauważ, że z powyższym, KEY12posortujesz przedtem, KEY2więc dostosuj sortpolecenie do swoich potrzeb.

don_crissti
źródło
2

Możesz użyć biblioteki stdlib Awk POSIX :

#!/usr/local/bin/awklib -f
$0 ~ "#" {x++}
{q[x] = q[x] ? q[x] RS $0 : $0}
END {
  arr_sort(q)
  for (x in q) print q[x]
}
Steven Penny
źródło