Ada segala macam alasan mengapa membaca seluruh file ke dalam ruang pola bisa salah. Masalah logika dalam pertanyaan seputar baris terakhir adalah yang umum. Hal ini terkait dengan sed
siklus garis - ketika tidak ada lagi garis dan sed
pertemuan EOF melalui - ia berhenti diproses. Dan jika Anda berada di baris terakhir dan Anda menginstruksikan sed
untuk mendapatkan yang lain itu akan berhenti di sana dan tidak melakukan lagi.
Yang mengatakan, jika Anda benar-benar perlu membaca seluruh file ke dalam ruang pola, maka mungkin ada baiknya mempertimbangkan alat lain pula. Faktanya adalah, sed
eponymously editor aliran - dirancang untuk bekerja garis - atau blok data yang logis - pada suatu waktu.
Ada banyak alat serupa yang lebih siap untuk menangani blok file lengkap. ed
dan ex
, misalnya, dapat melakukan banyak hal yang sed
dapat dilakukan dan dengan sintaksis yang sama - dan banyak lagi selain - tetapi daripada hanya beroperasi pada aliran input sambil mentransformasikannya menjadi output seperti sed
halnya, mereka juga memelihara file cadangan sementara dalam sistem file . Pekerjaan mereka buffered ke disk sesuai kebutuhan, dan mereka tidak berhenti secara tiba-tiba di akhir file (dan cenderung lebih jarang meledak di bawah tekanan buffer) . Selain itu mereka menawarkan banyak fungsi berguna yang sed
tidak - semacam itu tidak masuk akal dalam konteks aliran - seperti tanda garis, undo, bernama buffer, bergabung, dan banyak lagi.
sed
Kekuatan utama adalah kemampuannya untuk memproses data segera setelah membacanya - dengan cepat, efisien, dan dalam aliran. Ketika Anda menyeruput file, Anda membuangnya dan Anda cenderung mengalami kesulitan kasus tepi seperti masalah baris terakhir yang Anda sebutkan, dan buffer overruns, dan kinerja yang buruk - karena data yang diuraikannya bertambah panjang waktu pemrosesan mesin regexp saat menghitung pertandingan meningkat secara eksponensial .
Mengenai poin terakhir, omong-omong: sementara saya mengerti contoh s/a/A/g
kasus sangat mungkin hanya contoh naif dan mungkin bukan skrip sebenarnya yang ingin Anda kumpulkan dalam sebuah input, Anda mungkin akan merasa perlu waktu Anda untuk membiasakan diri dengan y///
. Jika Anda sering mendapati diri Anda g
menggantikan satu karakter dengan yang lain, maka y
itu bisa sangat berguna bagi Anda. Ini adalah transformasi yang bertentangan dengan substitusi dan jauh lebih cepat karena tidak menyiratkan regexp. Poin terakhir ini juga dapat berguna ketika mencoba untuk melestarikan dan mengulangi //
alamat kosong karena tidak memengaruhi mereka tetapi dapat dipengaruhi oleh mereka. Bagaimanapun, y/a/A/
adalah cara yang lebih sederhana untuk mencapai hal yang sama - dan swap juga dimungkinkan seperti:y/aA/Aa/
yang akan menukar semua huruf besar / kecil seperti pada garis untuk satu sama lain.
Anda juga harus mencatat bahwa perilaku yang Anda uraikan sebenarnya bukan apa yang seharusnya terjadi.
Dari GNU info sed
di bagian BUGS yang DILAPORKAN UMUM :
The POSIXLY_CORRECT
variabel lingkungan disebutkan karena POSIX menetapkan bahwa jika sed
pertemuan EOF ketika mencoba sebuah N
itu harus berhenti tanpa output, tapi versi GNU sengaja istirahat dengan standar dalam hal ini. Perhatikan juga bahwa meskipun perilaku tersebut dibenarkan di atas, anggapannya adalah bahwa kasus kesalahan adalah salah satu pengeditan aliran - tidak menyeruput seluruh file ke dalam memori.
The standar mendefinisikan N
's perilaku demikian:
N
Tambahkan baris input berikutnya, kurang garis \n
putusnya, ke ruang pola, menggunakan garis \n
tepi tertanam untuk memisahkan bahan yang ditambahkan dari bahan asli. Perhatikan bahwa nomor baris saat ini berubah.
Jika tidak ada baris input berikutnya yang tersedia, N
kata kerja perintah harus bercabang ke akhir skrip dan berhenti tanpa memulai siklus baru atau menyalin ruang pola ke output standar.
Pada catatan itu, ada beberapa GNU-isme lain yang diperlihatkan dalam pertanyaan - khususnya penggunaan :
label, b
peternakan, dan {
tanda kurung konteks fungsi }
. Sebagai aturan praktis setiap sed
perintah yang menerima parameter arbitrer dipahami membatasi pada \n
ewline dalam skrip. Jadi perintahnya ...
:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...
... semuanya sangat mungkin untuk bekerja secara tidak menentu tergantung pada sed
implementasi yang membacanya. Portabl mereka harus ditulis:
...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}
Hal yang sama berlaku untuk r
, w
, t
, a
, i
, dan c
(dan mungkin beberapa lagi yang saya lupa pada saat ini) . Dalam hampir setiap kasus mereka mungkin juga ditulis:
sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
"//{ do arbitrary list of commands" -e \}
... di mana -e
pernyataan eksekusi baru berdiri untuk \n
pembatas ewline. Jadi di mana info
teks GNU menyarankan implementasi tradisional sed
akan memaksa Anda untuk melakukan :
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
... itu seharusnya ...
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}
... tentu saja, itu tidak benar juga. Menulis naskah dengan cara itu agak konyol. Ada banyak cara sederhana untuk melakukan hal yang sama, seperti:
printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
//!g;x;$!d;:nd' -e 'l;$a\' \
-e 'this is the last line'
... yang mencetak:
foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line
... karena t
perintah est - seperti kebanyakan sed
perintah - tergantung pada siklus baris untuk menyegarkan register kembali dan di sini siklus baris diizinkan untuk melakukan sebagian besar pekerjaan. Itu adalah pengorbanan lain yang Anda lakukan ketika Anda menyeruput file - siklus baris tidak menyegarkan lagi, dan begitu banyak tes akan berperilaku tidak normal.
Perintah di atas tidak mengambil risiko input yang berlebihan karena hanya melakukan beberapa tes sederhana untuk memverifikasi apa yang dibaca saat membacanya. Dengan H
lama semua baris ditambahkan ke ruang pegang, tetapi jika garis cocok dengan /foo/
itu menimpa h
ruang lama. Buffer selanjutnya x
diubah, dan s///
substitusi bersyarat dicoba jika isi buffer sesuai dengan //
pola terakhir yang ditangani. Dengan kata lain, //s/\n/&/3p
upaya untuk mengganti baris baru ketiga di ruang yang ditahan dengan dirinya sendiri dan mencetak hasilnya jika ruang tunggu saat ini cocok /foo/
. Jika itu t
EST berhasil cabang naskah ke n
ot d
label apus - yang melakukan l
ook dan membungkus script.
Dalam hal kedua /foo/
dan baris baru ketiga tidak dapat dicocokkan bersama dalam ruang tunggu, maka //!g
akan menimpa buffer jika /foo/
tidak cocok, atau, jika cocok, itu akan menimpa buffer jika \n
ewline tidak cocok (sehingga menggantikan /foo/
dengan itu sendiri) . Tes halus kecil ini menjaga buffer dari mengisi tidak perlu untuk jangka panjang tidak /foo/
dan memastikan proses tetap tajam karena input tidak menumpuk. Menyusul dalam kasus tidak /foo/
atau //s/\n/&/3p
gagal buffer sekali lagi bertukar dan setiap baris tetapi yang terakhir ada dihapus.
Yang terakhir - baris terakhir $!d
- adalah demonstrasi sederhana tentang bagaimana sed
script top-down dapat dibuat untuk menangani banyak kasus dengan mudah. Ketika metode umum Anda adalah untuk memangkas kasus-kasus yang tidak diinginkan dimulai dengan yang paling umum dan bekerja ke arah yang paling spesifik maka kasus tepi dapat lebih mudah ditangani karena mereka hanya diperbolehkan masuk ke bagian akhir skrip dengan data yang Anda inginkan lainnya dan ketika semuanya membungkus Anda dengan data yang Anda inginkan. Namun, harus mengambil case edge dari loop tertutup bisa jauh lebih sulit untuk dilakukan.
Dan inilah hal terakhir yang harus saya katakan: jika Anda harus benar-benar menarik seluruh file, maka Anda dapat melakukan sedikit pekerjaan dengan mengandalkan siklus baris untuk melakukannya untuk Anda. Biasanya Anda akan menggunakan N
ext dan n
ext untuk lookahead - karena mereka maju sebelum siklus garis. Daripada menerapkan loop tertutup secara berulang dalam satu loop - karena sed
siklus hanya merupakan loop baca sederhana - jika tujuan Anda hanya untuk mengumpulkan input tanpa pandang bulu, maka mungkin lebih mudah untuk dilakukan:
sed 'H;1h;$!d;x;...'
... yang akan mengumpulkan seluruh file atau gagal mencoba.
catatan samping tentang N
dan perilaku baris terakhir ...
sementara saya tidak memiliki alat yang tersedia untuk saya uji, pertimbangkan bahwa N
ketika membaca dan mengedit di tempat berperilaku berbeda jika file yang diedit adalah file skrip untuk dibaca berikutnya.