Membaca seluruh file ke dalam ruang pola berguna untuk mengganti baris baru, & c. dan ada banyak contoh yang menyarankan hal berikut:
sed ':a;N;$!ba; [commands...]'
Namun, gagal jika input hanya berisi satu baris.
Sebagai contoh, dengan dua input baris, setiap baris dikenai perintah substitusi:
$ echo $'abc\ncat' | sed ':a;N;$!ba; s/a/xxx/g'
xxxbc
cxxxt
Tetapi, dengan input baris tunggal, tidak ada substitusi yang dilakukan:
$ echo 'abc' | sed ':a;N;$!ba; s/a/xxx/g'
abc
Bagaimana seseorang menulis sed
perintah untuk membaca semua input sekaligus dan tidak memiliki masalah ini?
sed -z
opsi GNU . Jika file Anda tidak memiliki null, itu akan dibaca hingga akhir file! Ditemukan dari ini: stackoverflow.com/a/30049447/582917Jawaban:
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 dansed
pertemuan EOF melalui - ia berhenti diproses. Dan jika Anda berada di baris terakhir dan Anda menginstruksikansed
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
danex
, misalnya, dapat melakukan banyak hal yangsed
dapat dilakukan dan dengan sintaksis yang sama - dan banyak lagi selain - tetapi daripada hanya beroperasi pada aliran input sambil mentransformasikannya menjadi output sepertised
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 yangsed
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 dengany///
. Jika Anda sering mendapati diri Andag
menggantikan satu karakter dengan yang lain, makay
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 :N
perintah di baris terakhirSebagian besar versi
sed
keluar tanpa mencetak apa pun ketikaN
perintah dikeluarkan pada baris terakhir file. GNUsed
mencetak ruang pola sebelum keluar kecuali tentu saja-n
saklar perintah telah ditentukan. Pilihan ini berdasarkan desain.Sebagai contoh, perilaku
sed N foo bar
akan tergantung pada apakah foo memiliki jumlah garis genap atau ganjil. Atau, ketika menulis skrip untuk membaca beberapa baris berikutnya mengikuti pencocokan pola, implementasi tradisionalsed
akan memaksa Anda untuk menulis sesuatu seperti/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
bukan hanya/foo/{ N;N;N;N;N;N;N;N;N; }
.Dalam kasus apa pun, solusi paling sederhana adalah dengan menggunakan
$d;N
skrip yang bergantung pada perilaku tradisional, atau untuk mengaturPOSIXLY_CORRECT
variabel ke nilai yang tidak kosong.The
POSIXLY_CORRECT
variabel lingkungan disebutkan karena POSIX menetapkan bahwa jikased
pertemuan EOF ketika mencoba sebuahN
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 setiapsed
perintah yang menerima parameter arbitrer dipahami membatasi pada\n
ewline dalam skrip. Jadi perintahnya ...... semuanya sangat mungkin untuk bekerja secara tidak menentu tergantung pada
sed
implementasi yang membacanya. Portabl mereka harus ditulis:Hal yang sama berlaku untuk
r
,w
,t
,a
,i
, danc
(dan mungkin beberapa lagi yang saya lupa pada saat ini) . Dalam hampir setiap kasus mereka mungkin juga ditulis:... di mana
-e
pernyataan eksekusi baru berdiri untuk\n
pembatas ewline. Jadi di manainfo
teks GNU menyarankan implementasi tradisionalsed
akan memaksa Anda untuk melakukan :... itu seharusnya ...
... tentu saja, itu tidak benar juga. Menulis naskah dengan cara itu agak konyol. Ada banyak cara sederhana untuk melakukan hal yang sama, seperti:
... yang mencetak:
... karena
t
perintah est - seperti kebanyakansed
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 menimpah
ruang lama. Buffer selanjutnyax
diubah, dans///
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 itut
EST berhasil cabang naskah ken
otd
label apus - yang melakukanl
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 bagaimanased
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 dann
ext untuk lookahead - karena mereka maju sebelum siklus garis. Daripada menerapkan loop tertutup secara berulang dalam satu loop - karenased
siklus hanya merupakan loop baca sederhana - jika tujuan Anda hanya untuk mengumpulkan input tanpa pandang bulu, maka mungkin lebih mudah untuk dilakukan:... yang akan mengumpulkan seluruh file atau gagal mencoba.
catatan samping tentang
N
dan perilaku baris terakhir ...sumber
H
terlebih dahulu itu menyenangkan.:a;$!{N;ba}
seperti yang saya sebutkan di atas - lebih mudah untuk menggunakan formulir standar dalam jangka panjang ketika Anda mencoba menjalankan regexps pada sistem yang tidak dikenal. Tapi itu tidak benar-benar apa yang saya maksudkan: Anda menerapkan loop tertutup - Anda tidak dapat dengan mudah masuk ke tengah-tengah itu ketika Anda ingin seperti yang Anda mungkin lakukan dengan bercabang - memangkas data yang tidak diinginkan - dan membiarkan siklus terjadi. Ini seperti hal top-down - semuanyased
dilakukan adalah akibat langsung dari apa yang baru saja dilakukan. Mungkin Anda melihatnya secara berbeda - tetapi jika Anda mencobanya, Anda mungkin akan menemukan skripnya lebih mudah.Gagal karena
N
perintah datang sebelum pola cocok$!
(bukan baris terakhir) dan berhenti sebelum melakukan pekerjaan apa pun:Ini dapat dengan mudah diperbaiki untuk bekerja dengan input single-line juga (dan memang untuk menjadi lebih jelas dalam hal apa pun) hanya dengan mengelompokkan
N
danb
perintah setelah pola:Ia bekerja sebagai berikut:
:a
buat label bernama 'a'$!
jika bukan baris terakhir, makaN
tambahkan baris berikutnya ke ruang pola (atau berhenti jika tidak ada baris berikutnya) danba
cabang (pergi ke) label 'a'Sayangnya, ini tidak portabel (karena bergantung pada ekstensi GNU), tetapi alternatif berikut (disarankan oleh @mikeserv) adalah portabel:
sumber
:a;N;$!ba;
.