Gambaran umum dari banyak jawaban yang ada dan bermanfaat , dilengkapi dengan penjelasan :
Contoh di sini menggunakan use case yang disederhanakan: ganti kata 'foo' dengan 'bar' di baris pertama yang cocok saja.
Karena penggunaan string ANSI C-dikutip ( $'...'
) untuk menyediakan jalur input sampel, bash
, ksh
, atau zsh
diasumsikan sebagai shell.
Hanya GNU sed
:
Jawaban Ben Hoffstein menunjukkan kepada kita bahwa GNU menyediakan ekstensi untuk spesifikasi POSIXsed
yang memungkinkan formulir 2-alamat berikut : 0,/re/
( re
mewakili ekspresi reguler yang sewenang-wenang di sini).
0,/re/
memungkinkan regex untuk mencocokkan pada baris pertama juga . Dengan kata lain: alamat seperti itu akan membuat rentang dari baris pertama hingga dan termasuk baris yang cocok re
- apakah re
terjadi pada baris pertama atau pada baris berikutnya.
- Bandingkan ini dengan formulir yang sesuai dengan POSIX
1,/re/
, yang membuat rentang yang cocok dari baris pertama hingga dan termasuk baris yang cocok re
dengan baris berikutnya ; dengan kata lain: ini tidak akan mendeteksi kemunculan pertama suatu re
kecocokan jika kebetulan terjadi pada baris pertama dan juga mencegah penggunaan steno//
untuk penggunaan kembali regex yang terakhir digunakan (lihat poin berikutnya). 1
Jika Anda menggabungkan 0,/re/
alamat dengan panggilan s/.../.../
(substitusi) yang menggunakan ekspresi reguler yang sama , perintah Anda hanya akan melakukan substitusi pada baris pertama yang cocok re
.
sed
menyediakan cara pintas yang nyaman untuk menggunakan kembali ekspresi reguler yang paling terakhir diterapkan : pasangan pembatas kosong//
,.
$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Hanya fitur POSIX sed
seperti BSD (macOS)sed
(juga akan berfungsi dengan GNU sed
):
Karena 0,/re/
tidak dapat digunakan dan formulir 1,/re/
tidak akan mendeteksi re
jika terjadi pada baris pertama (lihat di atas), penanganan khusus untuk baris 1 diperlukan .
Jawaban MikhailVS menyebutkan teknik tersebut, dimasukkan ke dalam contoh nyata di sini:
$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
catatan:
//
Pintasan regex kosong digunakan dua kali di sini: sekali untuk titik akhir rentang, dan sekali dalam s
panggilan; dalam kedua kasus, regex foo
secara implisit digunakan kembali, memungkinkan kita tidak harus menduplikatnya, yang membuat kode menjadi lebih pendek dan lebih mudah dikelola.
POSIX sed
membutuhkan baris baru aktual setelah fungsi tertentu, seperti setelah nama label atau bahkan penghilangannya, seperti halnya di t
sini; memisahkan skrip secara strategis menjadi beberapa -e
opsi merupakan alternatif untuk menggunakan baris baru yang sebenarnya: akhiri setiap -e
potongan skrip di mana baris baru biasanya perlu dituju.
1 s/foo/bar/
menggantikan foo
pada baris 1 saja, jika ditemukan di sana. Jika demikian, t
cabang ke akhir skrip (melompati perintah yang tersisa di baris). ( t
Fungsi bercabang ke label hanya jika s
panggilan terbaru melakukan substitusi yang sebenarnya; jika tidak ada label, seperti halnya di sini, akhir skrip bercabang ke).
Ketika itu terjadi, alamat rentang 1,//
, yang biasanya menemukan kejadian pertama mulai dari baris 2 , tidak akan cocok, dan rentang tidak akan diproses, karena alamat dievaluasi ketika baris saat ini sudah 2
.
Sebaliknya, jika tidak ada kecocokan di baris 1, 1,//
akan dimasukkan, dan akan menemukan kecocokan pertama yang sebenarnya.
Efek bersih adalah sama dengan GNU sed
's 0,/re/
: hanya kejadian pertama diganti, apakah itu terjadi di jalur 1 atau lainnya.
Pendekatan non-jangkauan
jawaban potong menunjukkan teknik loop yang memotong kebutuhan untuk rentang ; karena dia menggunakan sintaksis GNU sed
, berikut ini adalah padanan-POSIX-compliant :
Teknik loop 1: Pada pertandingan pertama, lakukan substitusi, lalu masukkan loop yang hanya mencetak garis yang tersisa apa adanya :
$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
Teknik loop 2, hanya untuk file bertubuh kecil : baca seluruh input ke dalam memori, kemudian lakukan penggantian tunggal di atasnya .
$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
1 1.61803 memberikan contoh tentang apa yang terjadi dengan 1,/re/
, dengan dan tanpa yang berikut s//
:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'
hasil $'1bar\n2bar'
; yaitu, kedua baris diperbarui, karena nomor baris 1
cocok dengan baris 1, dan regex /foo/
- akhir rentang - kemudian hanya dicari untuk memulai pada baris berikutnya . Oleh karena itu, kedua jalur dipilih dalam kasus ini, dan s/foo/bar/
penggantian dilakukan pada keduanya.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo'
Gagal : dengan sed: first RE may not be empty
(BSD / macOS) dan sed: -e expression #1, char 0: no previous regular expression
(GNU), karena, pada saat baris pertama sedang diproses (karena nomor baris 1
mulai rentang), belum ada regex yang diterapkan, jadi//
tidak mengacu pada apa pun.
Dengan pengecualian sintaks sed
khusus GNU 0,/re/
, rentang apa pun yang dimulai dengan nomor baris secara efektif menghalangi penggunaan //
.