Perbedaan antara sed pada Mac OSX dan sed "standar" lainnya?


Jawaban:


43

Perilaku utilitas shell berbeda dalam beberapa hal kecil antara varian unix. Ada banyak varian unix , dengan sejarah yang kompleks . Ada upaya standardisasi seperti standar POSIX dan supersetnya dengan spesifikasi UNIX Tunggal . Sebagian besar sistem saat ini menerapkan POSIX: 2001, juga dikenal sebagai Spesifikasi Single UNIX versi 3 , dengan penyimpangan kecil dan banyak ekstensi. Spesifikasi Single Unix bukan tutorial, tetapi versi 3 dapat dibaca jika Anda sudah memiliki gagasan tentang apa yang dilakukan perintah. Anda dapat berkonsultasi untuk mengetahui apakah beberapa fitur standar atau perpanjangan dari sistem tertentu.

Mayoritas pengguna unix menggunakan Linux dan belum menggunakan varian lain. Linux hadir dengan utilitas GNU , yang sering memiliki banyak ekstensi standar. Jadi, Anda akan menemukan cukup banyak kode di luar sana yang berfungsi di Linux tetapi tidak di kesatuan lain, karena bergantung pada ekstensi itu.

Mengenai sed, lihat spesifikasi sed Single Unix untuk minimum yang seharusnya didukung oleh setiap sistem, halaman manual pada sistem Anda untuk apa yang didukung oleh implementasi Anda, dan manual sed GNU untuk apa yang kebanyakan orang gunakan di luar sana.

Salah satu ekstensi tidak standar di GNU sed adalah mendukung banyak perintah yang berjalan bersama. Sebagai contoh, program sed GNU ini mencetak semua baris yang mengandung a, tetapi berubah bmenjadi yang cpertama:

sed -ne '/a/ {s/b/c/g; p}'

{dan }sebenarnya perintah yang terpisah, jadi untuk portabilitas penuh, Anda perlu menentukannya baik pada baris terpisah (dalam file) atau dalam -eargumen terpisah (pada baris perintah). Tidak adanya pemisah perintah setelah {dan penggunaan ;sebagai pemisah perintah adalah ekstensi umum. Kurangnya pemisah perintah sebelumnya }adalah ekstensi yang kurang umum. Ini sesuai standar:

sed -n -e '/a/ {' -e 's/b/c/g' -e p -e '}'

Ini tidak standar tetapi diterima secara umum:

sed -ne '/a/ { s/b/c/g; p; }'

Ekstensi lain yang tidak standar tetapi umum adalah penggunaan \nberarti baris baru dalam steks pengganti (penggunaan dalam regexp adalah standar). Metode portabel adalah memasukkan backslash-newline dalam skrip sed. Ekstensi umum lainnya adalah \+, \?dan \|dalam regexps berarti satu atau lebih, paling banyak satu dan bergantian; ekspresi reguler dasar portabel tidak memiliki ini. Sebagai contoh, perintah pertama adalah cara yang tidak portabel untuk mengganti urutan spasi yang berdekatan dengan baris baru; perintah kedua adalah yang setara dengan standar.

sed -e 's/ \+/\n/'
sed -e 's/  */\
/'

Perhatikan bahwa dalam semua kasus tentang ekstensi GNU, penggunaannya yang tidak standar. GNU seditu sendiri patuh karena melakukan hal-hal yang diperbolehkan (tetapi tidak wajib, tidak ditentukan) oleh standar. Ada kasus di mana itu tidak sesuai dan di mana menjalankannya dengan POSIXLY_CORRECTlingkungan dapat membantu. Suka dengan s/[\n]//gitu harus menghapus serangan balik dan nkarakter tetapi hapus baris baru sebagai gantinya. Atau perilaku Nperintah di baris terakhir.
Stéphane Chazelas

sed -ne '/a/ { s/b/c/g; p; }'adalah standar sejak edisi 2016 standar. Itu selalu portabel. Lihat austingroupbugs.net/view.php?id=944&nbn=7
Stéphane Chazelas

60

OS X saat ini hadir dengan sed FreeBSD dari tahun 2005. Sebagian besar perbedaan di bawah ini juga berlaku untuk versi sed BSD lainnya.

Penggunaan OS X -Euntuk ERE dan penggunaan GNU -r. -Eadalah alias untuk -rdalam GNU sed (ditambahkan pada 4.2, tidak didokumentasikan hingga 4.3). Versi FreeBSD dan NetBSD yang lebih baru mendukung keduanya -Edan -r. OpenBSD hanya mendukung sed -E.

-i ''bekerja dengan sed OS X tetapi tidak sed GNU. -ibekerja dengan GNU sed, versi terbaru NetBSD, OpenBSD sed, tetapi tidak untuk OS X. -i -ebekerja dengan keduanya tetapi dalam kasus FreeBSD sedmembuat cadangan dari file asli dengan -editambahkan ke nama file (dan Anda harus memberikan tidak lebih dari satu ekspresi ke sed).

GNU menafsirkan sed melarikan diri urutan seperti \t, \n, \001, \x01, \w, dan \b. OS X dan sed POSIX hanya menafsirkan \n(tetapi tidak di bagian penggantian s).

GNU sed menginterpretasikan \|,, \+dan \?dalam BRE tetapi OS X dan sed POSIX tidak. \(, \), \{, Dan \}adalah POSIX BRE.

GNU sed memungkinkan penghilangan ;atau baris baru sebelumnya }tetapi sed OS X tidak.

i(masukkan), a(tambahkan), dan c(ubah) harus diikuti oleh garis miring terbalik dan baris baru dalam OS X dan sed POSIX tetapi tidak dalam GNU sed. GNU sed menambahkan baris baru hilang setelah teks yang dimasukkan oleh i, aatau ctapi OS X sed tidak. Sebagai contoh sed 1iaadalah alternatif GNU untuk sed $'1i\\\na\n'.

Misalnya printf a|sed -n pmenambahkan baris baru di sed OS X tetapi tidak di sed GNU.

Sed OS X tidak mendukung pengubah I(case-insensitive) atau M(multi-line). Dukungan FreeBSD versi terbaru I.

Sed OS X tidak mendukung -s( --separate), -u( --unbuffered), atau -z( --null-data).

Salah satu opsi BSD yang tidak didukung oleh GNU sed adalah -a, yang membuat wmenambahkan ke file bukannya memotong file.

Contoh perintah sed GNU yang tidak berfungsi dengan sed OS X:

sed /pattern/,+2d # like `sed '/pattern/{N;N;d;}'`
sed -n 0~3p # like `awk NR%3==0`
sed /pattern/Q # like `awk '/pattern/{exit}1'` or `sed -n '/pattern/,$!p'`
sed 's/\b./\u&/g' # \u converts the next character to uppercase
sed 's/^./\l&/' # \l converts the next character to lowercase
sed -i '1ecat file_to_prepend' file # e executes a shell command
sed -n l0 # 0 disables wrapping

4
-i -etidak berfungsi di OSX. Itu menginterpretasikan -esebagai akhiran.
Chris Martin

3
@ ChrisMartin ya, dalam versi OS X -iselalu membutuhkan akhiran, bahkan jika string kosong. jadi -i '' -eharus bekerja.
waldyrious

@waldyrious Hanya berfungsi di OSX.
Chris Martin

ya, itu kekhasan dari versi itu :)
waldyrious

3
Kalimat " -i -ebekerja dengan keduanya." dalam jawaban Anda menunjukkan ada solusi lintas platform. Ternyata tidak ada.
leondepeon

5

Cara terbaik yang saya temukan untuk menjalankan skrip yang sama di Linux dan Mac adalah dengan:

sed -i.bak -e 's/foo/bar/' -- "${TARGET}" &&
  rm -- "${TARGET}.bak"

Atau gunakan dari perlmana -iasalnya. perl -Tpi -e 's/foo/bar/' -- "$TARGET"
Stéphane Chazelas
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.