Pengurutan paragraf kontinu yang dikelompokkan (dipisahkan oleh baris kosong)?

8

Saya pikir saya cukup berpengalaman sekarang dalam mengurutkan berdasarkan kolom ; Namun, saya belum menemukan apa pun sejauh ini bagaimana mengurutkan baris terus menerus .

Andaikata kita memiliki file teks yang terlihat seperti ini: (tentu saja sangat disederhanakan)

Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

Sekarang, apakah mungkin untuk mengurutkan garis secara alfanumerik per setiap blok secara terpisah ? Maksud saya, sehingga hasilnya terlihat seperti ini:

Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Bercerita dari apa yang saya temukan di sorthalaman manual, ini mungkin tidak dapat dilakukan dengan sortperintah UNIX bawaan. Atau bahkan dapat dilakukan tanpa harus menggunakan alat eksternal / pihak ketiga?

kesalahan sintaks
sumber

Jawaban:

9

Solusi Drav awkbaik, tetapi itu berarti menjalankan satu sortperintah per paragraf. Untuk menghindarinya, Anda bisa melakukan:

< file awk -v n=0 '!NF{n++};{print n,$0}' | sort -k1n -k2 | cut -d' ' -f2-

Atau Anda bisa melakukan semuanya dengan perl:

perl -ne 'if (/\S/){push@l,$_}else{print sort@l if@l;@l=();print}
          END{print sort @l if @l}' < file

Perhatikan bahwa di atas, pemisah adalah garis-garis kosong (untuk yang awksatu, garis-garis dengan hanya karakter spasi atau tab, untuk yang perlsatu, karakter spasi jarak horizontal atau vertikal) alih-alih garis kosong. Jika Anda ingin saluran kosong, Anda dapat mengganti !NFdengan !lengthatau $0=="", dan /\S/dengan /./.

Stéphane Chazelas
sumber
Terima kasih juga, terutama untuk awksolusi yang menghindari sortoverhead! Sneaky!
syntaxerror
9
awk -v RS= -v cmd=sort '{print | cmd; close(cmd); print ""}' file

Mengatur pemisah rekaman RSke string kosong membuat langkah awk dalam paragraf sekaligus. Untuk setiap paragraf, pipa paragraf (dalam $0) ke cmd (yang diatur ke sort) dan cetak hasilnya. Cetak baris kosong untuk memisahkan paragraf keluaran dengan a print "".

Jika kita memberikan contoh perl, maka saya menyajikan pendekatan alternatif daripada pendekatan Stephane:

perl -e 'undef $/; print join "\n", sort (split /\n/), "\n" 
    foreach(split(/\n\n/, <>))' < file

Hapus setel pemisah bidang ( undef $/), ini memungkinkan kami untuk menggunakan <>dan mendapatkan seluruh STDIN. Kami kemudian splitbahwa sekitar \n\n(paragraf). foreach"paragraf", sortbaris-baris dengan menyatukan splitbaris-baris baru, sortdan kemudian joinmenyatukannya kembali dan menempel pada sebuah garis \n.

Namun, ini memiliki satu efek samping menambahkan pemisah "paragraf tertinggal" pada paragraf terakhir (jika belum ada sebelumnya). Anda bisa menyiasatinya dengan yang sedikit kurang cantik:

perl -e 'undef $/; print join "\n", sort (split /\n/) , (\$_ == \$list[-1] ? "" : "\n")
    foreach(@list = split(/\n\n/, <>))' < file

Ini menugaskan paragraf ke @list, dan kemudian ada "operasi ternary" untuk memeriksa apakah itu adalah elemen terakhir dari foreach( \$_ == \$list[-1]cek). cetak ""jika ( ? ...), else ( : ...) cetak "\n"untuk semua "paragraf" (elemen @list) lainnya.

Drav Sloan
sumber
Ini rapi! Terima kasih. Apakah Anda benar-benar memohon /usr/bin/sortdengan garis itu atau apakah itu awkperintah "semacam" bawaan?
syntaxerror
Menjalankan perintah sortir, maka persyaratan untuk menutup (cmd) pada setiap loop :)
Drav Sloan
5

Saya menulis sebuah alat di haskell yang memungkinkan Anda untuk menggunakan sort, shuf, tac atau perintah lain pada paragraf teks.

https://gist.github.com/siers/01306a361c22f2de0122
EDIT: alat ini juga termasuk dalam repo ini: https://github.com/siers/haskell-import-sort

Ini membagi teks menjadi blok, bergabung dengan subblok dengan \0char, pipa melalui perintah dan akhirnya melakukan hal yang sama secara terbalik.

28-08-2015 : Saya menemukan penggunaan pribadi lain untuk alat ini - memilih N paragraf setelah baris.

paramap grep -aA2 '^reddit usernames' < ~/my-username-file
reddit usernames

foo
bar
baz

a couple
more of these
Raitis Veinbahs
sumber
4

Jika Anda memiliki GNU awk yang tersedia, Anda dapat mengurutkan setiap blok menggunakan asort()fungsi bawaan. Sesuatu seperti ini:

blocksort.awk

function sort_n_print(array) {
  asort(array)
  for(i=1; i<=length(array); i++)
    print array[i]
  delete array
}

NF { a[++x] = $0 }

!NF { sort_n_print(a); print }

END { sort_n_print(a) }

Jalankan seperti ini:

awk -f blocksort.awk infile
Thor
sumber
1

TXR Lisp langkah demi langkah:

$ cat data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(get-lines)' < data
("Echo" "Alpha" "Delta" "Charlie" "" "Golf" "Bravo" "Hotel" "Foxtrot")

$ txr -t '(get-lines)' < data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(partition* (get-lines) (op where [chain length zerop]))' < data
(("Echo" "Alpha" "Delta" "Charlie") ("Golf" "Bravo" "Hotel" "Foxtrot"))

$ txr -p '[mapcar sort (partition* (get-lines) (op where [chain length zerop]))]' < data
(("Alpha" "Charlie" "Delta" "Echo") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -p '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
(("Alpha" "Charlie" "Delta" "Echo") ("") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -t '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Referensi: dapatkan-baris , partisi * , op , di mana , rantai , panjang , zerop , mapcar , interpose .

Kaz
sumber
Perhatikan bahwa dalam [mapcar sort ...]kita bisa mengganti sortdengan fungsi yang menyalurkan string melalui proses eksternal. Kita kemudian dapat berakhir dengan alat untuk mendistribusikan perintah eksternal pemrosesan teks melalui paragraf.
Kaz