Bagaimana perintah sed '1! G; h; $! D' membalikkan isi file?

20

Pertanyaan saya terkait dengan sedsolusi-spesifik yang diberikan dalam jawaban ini untuk pertanyaan ini dari reverse grepping . The sed/ grepsolusi yang saya tidak dapat menguraikan adalah sebagai berikut satu:

sed '1!G;h;$!d' file

Dapatkah seseorang tolong menguraikan perintah ini?

Saya tahu dari pengetahuan VI (M) bahwa G menunjukkan baris terakhir dari file dan bahwa pada sed bang (!) Diikuti oleh alamat bekerja sedikit seperti grep -vitu adalah untuk mengatakan bahwa itu tidak akan cocok dengan baris itu. Tapi secara keseluruhan skrip inline di atas berada di luar saya.

Kutu buku
sumber
3
... sangat lambat ...
mikeserv
1
Sebagai petunjuk mikeserv. Orang harus mencatat bahwa sedresep rumit ini adalah cara yang sangat tidak efisien (O (n ^ 2/2) kompleksitas) untuk membalikkan baris dalam file. Akan sangat lambat untuk file dengan banyak baris. Untuk alternatif pembalikan urutan-urutan yang jauh lebih efisien lihat tacdari GNU coreutils.
arielf

Jawaban:

35

Ini membalikkan file baris demi baris.

sed '1! G; h; $! d' file

Pertama, sedmemiliki ruang tahan dan ruang pola . Kita harus membedakan antara mereka sebelum berkonsentrasi pada perintah khusus itu.

Ketika sedmembaca baris baru, itu dimuat ke dalam ruang pola. Oleh karena itu, ruang itu ditimpa setiap kali baris baru diproses. Di sisi lain, ruang penahanan konsisten untuk seluruh pemrosesan dan nilai-nilai dapat disimpan di sana untuk penggunaan selanjutnya.


Untuk perintah:

Ada 3 perintah dalam pernyataan ini: 1!G, hdan$!d

  • 1!Gberarti bahwa Gperintah dijalankan pada setiap baris kecuali yang pertama (yang !meniadakan 1). Gberarti menambahkan apa yang ada di ruang pegang ke dalam ruang pola.

  • hberlaku untuk setiap baris. Ini menyalin ruang pola ke ruang pegang (dan menimpanya).

  • $!dberlaku untuk setiap baris kecuali yang terakhir ( $mewakili baris terakhir, !meniadakannya). dadalah perintah untuk menghapus garis (pola spasi).


  1. Sekarang, ketika baris pertama dibaca, sedjalankan hperintah. Baris pertama disalin ke ruang tunggu. Kemudian dihapus, karena cocok dengan $!kondisi. sedmelanjutkan dengan baris kedua.
  2. Baris kedua cocok dengan kondisi 1!(itu bukan baris pertama), dan jadi ruang penahanan (yang memiliki baris pertama) ditambahkan ke ruang pola (yang memiliki baris kedua). Setelah itu, dalam ruang pola, sekarang ada baris kedua diikuti oleh baris pertama, dibatasi oleh baris baru. Sekarang, hperintah berlaku (seperti di setiap baris); semua yang ada dalam pola ruang disalin ke ruang tunggu. Pernyataan ketiga ( $!d) berlaku: Baris dihapus dari ruang pola.
  3. Langkah 2 sekarang dilakukan dengan semua lini. Kami melompat ke baris terakhir.
  4. Di baris terakhir ( $) hampir semua Langkah 2 dilakukan, tetapi bukan bagian hapus ( d). sed, ketika dipanggil tanpa -n, mencetak ruang pola secara otomatis di akhir pemrosesan untuk setiap jalur input. Jadi, ketika tidak dihapus, ruang pola dicetak. Ini sekarang berisi semua baris dalam urutan terbalik .
kekacauan
sumber
1
@Geek Tidak, hperintah menyalin ruang pola ke ruang tunggu, yang bertahan sampai sedberakhir. Setelah akhir skrip semuanya dibersihkan, karena biner keluar.
kekacauan
2
Bisakah kita memikirkan ruang penahanan seperti register untuk vim? Apakah mereka juga diberi nomor? Atau hanya ada satu saja?
Geek
2
@Geek In sedhanya ada satu ruang tunggu. Ini seperti variabel yang dapat berisi sesuatu.
kekacauan
1
@ user1717828 Jika tidak, baris pertama akan dicetak, saat diproses. Karena sed tidak dipanggil dengan, -nkita harus menghapus setiap baris kecuali yang terakhir. Pada baris terakhir sed menambahkan semua dari ruang pegang ke ruang pola. Dan karena dperintah tidak akan dieksekusi, baris dicetak (baris ini sekarang berisi seluruh file terbalik).
kekacauan
1
(1) Sub-pertanyaan / pengamatan OP (dalam komentar) adalah, "jadi hperintah pada baris terakhir adalah semacam no-op." (Dengan penekanan tambahan, dan sedikit diparafrasekan). Dia benar; ketika sedmemproses baris terakhir input , Gmembaca ruang penahanan (untuk menambahkannya ke ruang pola), dan kemudian hmenyalin ruang pola ke ruang penahanan, yang tidak pernah direferensikan lagi . Kita bisa saja mengatakan sed 'G;$!h;$!d'atau sed 'G;$!{h;d}'. (2) Kita bisa menghindari menggunakan ddengan mengatakan sed -n 'G;h;$p'.
Scott