Sortir file teks dengan beberapa baris sebagai satu baris

13

Saya memiliki file teks dalam format ini:

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

Saya ingin menyortir file ini dengan KEYbaris dan menjaga 4 baris berikutnya dengan itu sehingga hasil diurutkan harus:

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

apakah ada cara untuk melakukan ini?

RYN
sumber
5
tidak lintas pasca silakan
Zanna
@Zanna: Saya pikir ada pengecualian untuk bagian unix dan askubuntu karena keduanya memiliki banyak tumpang tindih dengan satu sama lain! Saya rasa saya membaca tentang ini di bagian meta unix
RYN
2
pertanyaan meta yang relevan diajukan di sini oleh AU mod :) Bagaimana seharusnya pertanyaan lintas-posting di Tanya Ubuntu ditangani?
Zanna
@RYN Masalahnya bukan tumpang tindih, sebenarnya banyak situs SE tumpang tindih, tetapi orang yang memberikan jawaban mungkin tidak tahu tentang jawaban di situs lain.
phk

Jawaban:

12

msort(1)dirancang untuk dapat mengurutkan file dengan catatan multi-line. Ini memiliki gui opsional, serta versi baris perintah normal dan dapat digunakan untuk manusia. (Setidaknya, manusia yang suka membaca manual dengan hati-hati dan mencari contoh ...)

AFAICT, Anda tidak bisa menggunakan pola arbitrer untuk catatan, jadi kecuali catatan Anda berukuran tetap (dalam byte, bukan karakter atau baris). msortmemang memiliki -bopsi untuk catatan yang merupakan blok garis yang dipisahkan oleh garis kosong.

Anda dapat mengubah input Anda menjadi format yang akan bekerja dengan -bcukup mudah, dengan meletakkan baris kosong di depan setiap ###...(kecuali yang pertama).

Secara default, ini mencetak statistik pada stderr, jadi setidaknya mudah untuk mengetahui kapan tidak mengurutkan karena mengira seluruh input adalah satu catatan.


msortbekerja pada data Anda. The sedperintah menambahkan sebuah baris baru untuk setiap #+baris kecuali baris 1. -wmacam seluruh catatan (leksikografi). Ada beberapa opsi untuk memilih bagian rekaman mana yang akan digunakan sebagai kunci, tetapi saya tidak membutuhkannya.

Saya juga meninggalkan stripping baris baru ekstra.

$ 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

Saya tidak beruntung -r '#'menggunakannya sebagai pemisah rekaman. Itu mengira seluruh file adalah satu catatan.

Peter Cordes
sumber
Terima kasih banyak; msortsangat berguna; terima kasih ( -rsepertinya itu karena ada lebih dari satu # yang saya gunakan -ddan berhasil
RYN
keren! (+1) msort -qwr '#' ex bekerja untuk saya (baik itu memisahkan pemisah rec output)
JJoao
8

Solusi adalah pertama-tama mengubah umpan baris di dalam blok ke karakter yang tidak digunakan pilihan Anda ('|' dalam contoh di bawah), untuk mengurutkan hasilnya dan mengubah kembali pemisah yang dipilih ke umpan baris asli:

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'
xienne
sumber
1
Terima kasih; ini berfungsi tetapi sangat kotor khususnya ketika datanya juga kotor! jika garis setelah kuncinya adalah 100 maka saya perlu meletakkan 100 di ;Nsana, dan itu bisa sulit untuk menemukan karakter yang tidak digunakan dalam teks itu sendiri; ini sangat baik untuk sortatau awk, ... dapat melakukan penyortiran multiline
RYN
4
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 slurps seluruh file
  • /(....)/g cocokkan dan ekstrak catatan
  • print sort ... urutkan dan cetaklah
Joao
sumber
2

Berikut cara lain yang dapat digunakan dengan sejumlah baris di KEYbagian:

# 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}'

Ini berfungsi dengan menyimpan pembatas ke variabel (untuk kemudian menghapusnya dari input). Kemudian menambahkan KEY*ke setiap baris di bagian yang sesuai menggunakan char ascii rendah (yang tidak mungkin terjadi pada input Anda) sebagai pemisah dan kemudian number semua lines menggunakan pemisah yang sama. Maka hanya masalah sortdengan bidang 3 dan 1 dan cutting kolom tengah dan kemudian mengembalikan pembatas melalui final sed. Perhatikan bahwa dengan hal di atas, KEY12akan mengurutkan sebelum KEY2jadi sesuaikan sortperintah sesuai kebutuhan Anda.

don_crissti
sumber
2

Anda dapat menggunakan perpustakaan stdlib POSIX Awk :

#!/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
sumber