Dalam komentar untuk pertanyaan ini muncul kasus di mana berbagai implementasi sed tidak setuju pada program yang cukup sederhana, dan kami (atau setidaknya saya) tidak dapat menentukan spesifikasi yang sebenarnya diperlukan untuk itu.
Masalahnya adalah perilaku rentang yang dimulai pada baris yang dihapus:
1d;1,2d
Haruskah baris 2 dihapus meskipun awal rentang telah dihapus sebelum mencapai perintah itu? Harapan awal saya adalah "tidak" sejalan dengan sed BSD, sementara GNU sed mengatakan "ya", dan memeriksa teks spesifikasi tidak sepenuhnya menyelesaikan masalah.
Sesuai harapan saya adalah (setidaknya) macOS dan Solaris sed
, dan BSD sed
. Yang tidak setuju adalah (setidaknya) GNU dan Busybox sed
, dan banyak orang di sini. Dua yang pertama bersertifikat SUS sementara yang lain lebih luas. Perilaku mana yang benar?
The spesifikasi teks untuk rentang dua alamat mengatakan:
The sed utilitas kemudian berlaku secara berurutan semua perintah yang alamat pilih ruang yang pola, sampai perintah mulai siklus berikutnya atau berhenti.
dan
Perintah pengeditan dengan dua alamat harus memilih rentang inklusif dari ruang pola pertama yang cocok dengan alamat pertama melalui ruang pola berikutnya yang cocok dengan yang kedua. [...] Mulai dari baris pertama mengikuti rentang yang dipilih, sed akan mencari lagi untuk alamat pertama. Setelah itu, proses harus diulang.
Dapat diperdebatkan, baris 2 berada dalam "rentang inklusif dari ruang pola pertama yang cocok dengan alamat pertama melalui ruang pola berikutnya yang cocok dengan yang kedua", terlepas dari apakah titik awal telah dihapus. Di sisi lain, saya mengharapkan yang pertama d
untuk melanjutkan ke siklus berikutnya dan tidak memberikan rentang kesempatan untuk memulai. Implementasi bersertifikasi UNIX ™ melakukan apa yang saya harapkan, tetapi berpotensi tidak sesuai dengan mandat spesifikasi.
Beberapa eksperimen ilustratif mengikuti, tetapi pertanyaan kuncinya adalah: apa yang harus sed
dilakukan ketika rentang dimulai pada baris yang dihapus?
Eksperimen dan contoh
Demonstrasi yang disederhanakan dari masalah ini adalah ini, yang mencetak salinan garis lebih banyak daripada menghapusnya:
printf 'a\nb\n' | sed -e '1d;1,2p'
Ini menyediakan sed
dua jalur input, a
dan b
. Program ini melakukan dua hal:
Menghapus baris pertama dengan
1d
. Thed
perintah akanHapus ruang pola dan mulai siklus berikutnya. dan
- Pilih rentang garis dari 1 hingga 2 dan cetak secara eksplisit, selain pencetakan otomatis yang diterima setiap baris. Garis yang termasuk dalam rentang dengan demikian akan muncul dua kali.
Harapan saya adalah ini harus dicetak
b
hanya, dengan rentang yang tidak berlaku karena 1,2
tidak pernah tercapai selama baris 1 (karena sudah d
melompat ke siklus / baris berikutnya) dan dengan demikian jangkauan inklusi tidak pernah dimulai, sementara a
telah dihapus. Unix sed
s dari macOS dan Solaris 10 menghasilkan output ini, seperti halnya non-POSIX sed
di Solaris dan BSD sed
secara umum.
GNU sed, di sisi lain, mencetak
b
b
menunjukkan bahwa ia telah menafsirkan kisaran. Ini terjadi baik dalam mode POSIX dan tidak. Busybox's sed memiliki perilaku yang sama (tetapi perilaku yang tidak selalu identik, sehingga sepertinya bukan hasil dari kode bersama).
Eksperimen lebih lanjut dengan
printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/c/p'
printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/d/p'
menemukan bahwa itu tampaknya memperlakukan rentang yang dimulai pada baris yang dihapus seolah-olah itu dimulai pada baris berikut . Ini terlihat karena /c/
tidak cocok untuk mengakhiri rentang. Menggunakan /b/
untuk memulai rentang tidak berperilaku sama dengan 2
.
Contoh kerja awal yang saya gunakan adalah
printf '%s\n' a b c d e | sed -e '1{/a/d;};1,//d'
sebagai cara untuk menghapus semua baris hingga /a/
pertandingan pertama , bahkan jika itu ada di baris pertama (apa yang akan digunakan oleh GNU 0,/a/d
- ini adalah percobaan yang kompatibel dengan POSIX untuk itu).
Disarankan bahwa ini seharusnya menghapus hingga kecocokan kedua/a/
jika baris pertama cocok (atau seluruh file jika tidak ada kecocokan kedua), yang tampaknya masuk akal - tetapi sekali lagi, hanya sed GNU yang melakukan itu. Baik sed macOS dan sed solaris
b
c
d
e
untuk itu, seperti yang saya perkirakan (GNU sed menghasilkan output kosong dari menghapus rentang yang tidak ditentukan; sed Busybox mencetak hanya d
dan e
, yang jelas salah apa pun). Secara umum saya akan berasumsi bahwa mereka telah lulus tes kesesuaian sertifikasi berarti bahwa perilaku mereka benar, tetapi cukup banyak orang menyarankan sebaliknya bahwa saya tidak yakin, teks spesifikasi tidak sepenuhnya meyakinkan, dan test suite tidak dapat sangat komprehensif.
Jelas itu tidak praktis portabel untuk menulis kode hari ini mengingat inkonsistensi, tetapi secara teoritis harus setara di mana-mana dengan satu makna atau yang lain. Saya pikir ini adalah bug, tapi saya tidak tahu implementasi mana yang harus dilaporkan. Pandangan saya saat ini adalah bahwa perilaku sed GNU dan Busybox tidak konsisten dengan spesifikasi, tapi saya bisa salah tentang itu.
Apa yang dibutuhkan POSIX di sini?
ed
, melewatised
semuanya?Jawaban:
Itu diangkat di milis grup Austin pada Maret 2012. Inilah pesan terakhirnya (oleh Geoff Clare dari Austin Group (badan yang mengelola POSIX), yang juga merupakan orang yang mengangkat masalah ini sejak awal). Di sini disalin dari antarmuka NNTP gmane:
Dan inilah bagian yang relevan dari sisa pesan (oleh saya) yang dikutip Geoff:
Jadi, (menurut Geoff) POSIX jelas bahwa perilaku GNU tidak sesuai.
Dan memang benar itu kurang konsisten (dibandingkan
seq 10 | sed -n '1d;1,2p'
denganseq 10 | sed -n '1d;/^1$/,2p'
) bahkan jika berpotensi kurang mengejutkan bagi orang-orang yang tidak menyadari bagaimana rentang diproses (bahkan Geoff awalnya menemukan perilaku yang sesuai "aneh" ).Tidak ada yang peduli melaporkannya sebagai bug ke orang-orang GNU. Saya tidak yakin saya akan memenuhi syarat sebagai bug. Mungkin opsi terbaik adalah untuk memperbarui spesifikasi POSIX untuk memungkinkan kedua perilaku untuk memperjelas bahwa seseorang tidak dapat mengandalkan keduanya.
Edit . Saya sekarang telah melihat
sed
implementasi asli di Unix V7 dari akhir 70-an, dan sepertinya perilaku untuk alamat numerik tidak dimaksudkan atau setidaknya tidak dipikirkan sepenuhnya di sana.Dengan Geoff membaca spec (dan interpretasi asli saya tentang mengapa hal itu terjadi), sebaliknya, di:
baris 1, 2, 4 dan 5 harus berupa output, karena kali ini, alamat akhir yang tidak pernah ditemui oleh
1,3p
perintah jarak jauh, seperti diseq 5 | sed -n '3d;/1/,/3/p'
Namun, itu tidak terjadi pada implementasi asli, atau implementasi lain yang saya coba (busybox
sed
mengembalikan baris 1, 2 dan 4 yang lebih mirip bug).Jika Anda melihat kode UNIX v7 , itu memeriksa kasus di mana nomor baris saat ini lebih besar dari alamat akhir (numerik), dan keluar dari jangkauan itu. Fakta bahwa ia tidak melakukannya untuk alamat awal lebih mirip pengawasan daripada desain yang disengaja.
Apa itu artinya adalah bahwa tidak ada implementasi yang benar-benar sesuai dengan interpretasi dari spesifikasi POSIX dalam hal ini saat ini.
Perilaku membingungkan lainnya dengan implementasi GNU adalah:
Karena baris 2 dilewati, maka
2,/3/
baris 3 dimasukkan (baris pertama yang angkanya> = 2). Tetapi karena garis itulah yang membuat kami memasuki rentang, itu tidak memeriksa alamat akhir . Menjadi lebih buruk denganbusybox sed
di:Karena baris 2 hingga 7 dihapus, baris 8 adalah yang pertama yaitu> = 2 sehingga rentang 2,3 dimasukkan kemudian!
sumber
seq 10 | sed -n '1d;1,2p'
denganseq 10 | sed -n '1d;/^1$/,2p'
) bahkan jika berpotensi kurang mengejutkan bagi orang-orang tidak akan menyadari bagaimana rentang diproses. Tidak ada yang peduli melaporkannya sebagai bug ke orang-orang GNU. Saya tidak yakin saya akan memenuhi syarat sebagai bug, mungkin pilihan terbaik adalah memperbarui spesifikasi POSIX untuk memungkinkan kedua perilaku untuk memperjelas bahwa seseorang tidak dapat mengandalkan keduanya.d
tidak hanya masalah kinerja, itu mengarah ke masalah implementasi lebih lanjut karena pola "tak terlihat" yang diperlukan untuk rentang tidak diperbolehkan memiliki efek pada pola kosong lebih lanjut ... berantakan!1d;1,2p
skrip itu1,2p
perintah tidak dijalankan pada baris pertama, sehingga alamat pertama tidak cocok dengan ruang pola apa pun , yang merupakan salah satu cara untuk menafsirkan teks itu. Bagaimanapun, harus jelas bahwa evaluasi alamat harus dibuat pada saat perintah dijalankan. Seperti dised 's/./x/g; /xxx/,/xxx/d'
1
dan/1/
keduanya alamat,1
adalah alamat ketika nomor baris 1,/1/
adalah alamat ketika ruang pola berisi1
, pertanyaannya adalah apakah kedua jenis alamat harus diperlakukan sama, atau jika rentang nomor baris harus dipertimbangkan " dalam absolut "terlepas dari apakah mereka cocok atau tidak. Lihat juga hasil edit terakhir saya untuk konteks yang lebih historis.