sed pada OSX masukkan pada baris tertentu

24

Jadi saya telah menggunakan 'sed' di linux untuk sementara waktu, tetapi telah mengalami sedikit kesulitan mencoba menggunakannya di OSX sejak 'POSIX sed' dan 'GNU sed' memiliki banyak perbedaan kecil. Saat ini saya sedang berjuang dengan cara memasukkan satu baris teks setelah nomor baris tertentu. (dalam hal ini, baris 4)

Di linux saya akan melakukan sesuatu seperti ini:

sed --in-place "4 a\  mode '0755'" file.txt

Jadi pada OSX saya mencoba ini:

sed -i "" "4 a\ mode '0755'" file.txt

Namun ini terus memberi saya kesalahan 'karakter tambahan setelah \ di akhir perintah'. Ada ide apa yang salah di sini? Apakah saya salah ketik? Atau apakah saya tidak mengerti perbedaan lain antara versi sed?

CRThaze
sumber
2
Dapat mengonfirmasi ini untuk tidak bekerja pada MacOS 10.6.8. Bekerja dengan baik pada debian 6.0.3.
tink

Jawaban:

24

Sebenarnya, spesifikasi POSIX untuksed memerlukan baris baru setelah a\:

[1addr]a\
text

Tulis teks ke output standar seperti yang dijelaskan sebelumnya.

Hal ini membuat menulis satu kalimat sedikit rasa sakit, yang mungkin merupakan alasan berikut ekstensi GNU ke a, idan cperintah:

Sebagai ekstensi GNU, jika antara adan baris baru ada selain \urutan spasi-putih , maka teks dari baris ini, dimulai dari karakter non-spasi pertama setelah a, diambil sebagai baris pertama dari blok teks . (Ini memungkinkan penyederhanaan dalam pembuatan skrip untuk penambahan satu baris.) Ekstensi ini juga berfungsi dengan idan cperintah.

Jadi, untuk menjadi portabel dengan sedsintaks Anda, Anda harus menyertakan baris baru setelah a\entah bagaimana. Cara termudah adalah dengan hanya memasukkan baris baru yang dikutip:

$ sed -e 'a\
> text'

(di mana $dan >apakah shell meminta). Jika Anda merasa sakit, bash[1] memiliki $' 'sintaks kutipan untuk menyisipkan lolos gaya-C, jadi gunakan saja

sed -e 'a\'$'\n''text'

[1] dan mksh (r39b +) dan beberapa shell bourne non-bash (mis., / Bin / sh dalam FreeBSD 9+)

jw013
sumber
Jawaban yang sangat informatif. Terima kasih atas petunjuk tentang sintaks bash quoting.
cjackson
14

Seperti yang dijelaskan dalam jawaban ini , akan sangat membantu ketika menggunakan perintah sed seperti idan amenggunakan beberapa -e "..."klausa. Masing-masing klausa ini akan dipahami dipisahkan oleh baris baru. The idan aperintah sulit untuk digunakan dalam inline sed script sebaliknya (mereka dirancang untuk digunakan dalam multiline sed file script dipanggil menggunakan sed -f file ...). Sepertinya Anda tidak dapat menggunakan baris baru tersirat yang diperkenalkan pada akhir -eklausa untuk memisahkan a\dan baris teks yang akan ditambahkan. Tetapi Anda dapat menggunakannya untuk mengakhiri baris teks yang akan ditambahkan.

Dalam kasus khusus ini, apa yang Anda coba lakukan sebenarnya bisa diselesaikan dengan satu -e ...klausa. Anda hanya perlu menggunakan aperintah dengan benar. Dengan standar POSIX, aperlu diikuti oleh \, maka yang perlu diikuti oleh baris baru, maka sisa baris berikutnya akan dimasukkan (sampai baris baru atau akhir -eklausa ditemui). Jadi Anda bisa melakukan:

sed -i "" -e $'4 a\\\n'"mode '0755'" file.txt
dubiousjim
sumber
2
Jika Anda ingin memilah-milah fitur apa yang Anda andalkan adalah Gnu-isme, halaman ini dapat membantu: wiki.alpinelinux.org/wiki/Regex . Ini terbatas hanya pada fitur regex; bukan perintah sed lainnya.
dubiousjim
5

GNU sed tampaknya jauh lebih umum digunakan daripada POSIX, berdasarkan contoh / tutorial yang saya gunakan.

Saya telah menemukan bahwa jauh lebih mudah hanya menginstal sed GNU pada mesin OSX apa pun yang saya gunakan. Jika Anda tertarik, cara terbaik untuk melakukannya adalah menginstalnya melalui Homebrew .

Anda bisa saja $ brew install gnu-sed, atau mendapatkan semua utilitas umum GNU $ brew install coreutils.

Kemudian ketika Anda mengalami masalah sintaks dengan dateatau beberapa program lain, Anda bisa menggunakan versi GNU. Akhirnya saya memutuskan lebih mudah untuk selalu menggunakan versi GNU dan menempatkannya lebih awal dari versi sistem di Windows PATH.

Dekan
sumber
5

Perl tidak menderita dari keanehan yang tergantung pada platform seperti GNU vs. non-GNU vs. "eksklusif". Anda bisa melakukannya:

perl -ni.old -e 'print;if ($.==4) {print "mode 0755\n"}' file

The -n' option creates a loop that reads every line of the input file. Unlike its cousin-p (not used here) it doesn't automatically print every line read. The-i invokes in-place replacement. The argument to-i (viz. ".old") can be dropped or changed. It leaves a backup of the unmodified file. The -esinyal awal dari script.

Yang $.menunjukkan nomor baris, dimulai dengan baris-1. Dengan demikian baris perintah membaca 'file' dan ketika nomor baris sama dengan 4, itu mencetak baris yang diikuti oleh apa pun yang Anda ingin menyuntikkan.

Saya akan segera menambahkan bahwa Perl berakar pada akarnya sed, AWK dan C, jadi sintaksis untuk substitusi sederhana, dll. Bukan kurva belajar yang sangat curam.

JRFerguson
sumber
Jawaban bagus! Tetapi Anda dapat menyederhanakannya sedikit. Coba ini: perl -wlpi.old -e 'print "mode 0755" if $. == 5' file.txt. The -pberalih bekerja baik di sini (karena kita ingin mencetak setiap baris); kita hanya perlu mengubah logika dari "mencetak setelah baris 4" menjadi "mencetak sebelum baris 5". Dan -lsaklar membuatnya sehingga "\ n" dicetak secara otomatis dengan setiap print, sehingga Anda tidak perlu mencetaknya sendiri.
JL