Apakah ada alternatif untuk perintah "sed -i" di Solaris?

11

Saya memiliki persyaratan dalam proyek saya untuk mengganti beberapa teks yang ada dalam file seperti foodengan beberapa teks lain seperti fooofoo:

abc.txt
name
foo
foo1

Jadi saya mencoba:

sed -i "s/foo/fooofoo/g" abc.txt

Namun saya mendapatkan kesalahan ini:

sed: opsi ilegal - i

Saya menemukan dalam manual yang harus saya gunakan:

sed -i\ "s/foo/fooofoo/g" abc.txt

Namun ini juga tidak berhasil.

Saya menemukan alternatif perldan awkjuga tetapi solusi di Solaris sedakan sangat dihargai.

Saya menggunakan versi bash ini:

GNU bash, versi 3.2.57 (1) -release (sparc-sun-solaris2.10)

tpsaitwal
sumber
Pada Solaris 11 dan yang lebih baru, cukup gunakan /usr/gnu/bin/seduntuk mendapatkan -i dukungan.
alanc

Jawaban:

15

Gunakan ed. Ini tersedia di sebagian besar platform dan dapat mengedit file Anda di tempat.
Karena seddidasarkan pada edsintaks untuk mengganti pola adalah serupa:

ed -s infile <<\IN
,s/old/new/g
w
q
IN
don_crissti
sumber
Kenapa edbukannya ex, penasaran saja? (Saya tahu keduanya kompatibel dengan POSIX dan saya telah ditautkan ke spesifikasi.;))
Wildcard
1
@ Kartu Memori - Saya bisa bertanya sebaliknya - mengapa ex? Untuk menjawab pertanyaan Anda: karena saya tidak pernah menggunakan, ex saya tidak terbiasa dengannya. btw, saya telah melihat setup di mana edada tetapi extidak (tapi tidak sebaliknya) ... yang paling terkenal adalah laptop saya :)
don_crissti
Jawaban yang bagus. :) Jawaban saya: Karena saya menggunakan Vim sepanjang waktu, saya sangat akrab dengan experintah. Semua viperintah yang bisa Anda ketik yang dimulai dengan titik dua adalah experintah. Tentu saja ada beberapa ekstensi khusus Vim, tetapi hanya serangkaian perintah inti yang sangat kuat dan fleksibel dan banyak untuk pengeditan skrip.
Wildcard
Sistem Manjaro saya exbelum dan belum ed.
pertengahan
8

Jika Anda tidak dapat menginstal sed GNU, gunakan:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

Ini menggunakan pengalihan untuk mengirim output sed ke file sementara. Jika sed selesai dengan sukses, maka ini menimpa abc.txtdengan file sementara.

Seperti yang dapat dilihat dari kode sumber untuk GNU sed , inilah yang sed -idilakukannya. Jadi, ini hampir seefisien sed -i.

Jika ada peluang yang abc.tmpsudah ada, maka Anda mungkin ingin menggunakan mktempatau utilitas serupa untuk menghasilkan nama unik untuk sementara.

John1024
sumber
Terimakasih atas bantuannya. Namun, apakah ada opsi dalam solaris dimana kita dapat memperbarui file yang ada tanpa membuat file temp?
tpsaitwal
10
Benci untuk membaginya kepada Anda, tetapi bahkan sed membuat file sementara. git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
Jeff Schaller
1
Anda dapat melakukannya jika Anda tidak peduli dengan tautan keras - lihat contoh saya. Masih ada file sementara, tetapi disembunyikan (tidak disebutkan namanya). Tanpa sementara, Anda harus menggunakan ed, karena itu membaca seluruh file ke dalam memori.
Toby Speight
3

Jika Anda ingin yang setara sed -i.bak, itu sangat sederhana.

Pertimbangkan skrip ini untuk GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

Kami cukup mengganti baris yang ditandai dengan

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

Ini memindahkan file yang ada menjadi cadangan, dan menulis file baru.

Jika kita ingin yang setara

sed -i -e "$script" "$dir"  # no backup

maka itu sedikit lebih kompleks. Kita dapat membuka file untuk dibaca sebagai input standar, kemudian memutuskan tautannya, sebelum mengarahkan output sed untuk menggantinya:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

Kami melakukan ini dalam sub-shell, sehingga stdin asli kami masih tersedia setelah ini. Dimungkinkan untuk beralih input dan beralih kembali tanpa subshell, tetapi cara ini tampaknya lebih jelas bagi saya.

Perhatikan bahwa kami berhati-hati untuk menyalin terlebih dahulu, daripada membuat foofile baru - ini penting jika file tersebut diketahui oleh lebih dari satu nama (yaitu memiliki tautan keras) dan Anda ingin memastikan bahwa Anda tidak memutus tautan tersebut .

Toby Speight
sumber
3

Pertama, Anda harus tahu bahwa i\perintah yang Anda maksud adalah untuk menyisipkan baris teks — bukan untuk menyimpan teks yang diedit kembali ke file. Tidak ada cara yang ditentukan POSIX untuk menggunakannya sed.

Apa yang Anda bisa lakukan adalah menggunakan ex, yang adalah ditentukan oleh POSIX :

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

The xperintah untuk "menyimpan dan keluar." Anda juga bisa menggunakan wqtetapi xlebih pendek.

The %tanda pada awal sarana perintah pengganti, "Terapkan perintah ini untuk setiap baris dalam buffer." Anda bisa menggunakannya 1,$s/find/replace/gjuga.


Satu perbedaan utama antara exdan seditu sedadalah editor aliran . Ini hanya beroperasi secara berurutan, baris demi baris. exjauh lebih fleksibel daripada itu, dan sebenarnya Anda dapat melakukan pengeditan file teks interaktif secara langsung di ex. Ini adalah pendahulu langsung untuk vi.

Wildcard
sumber
1

Menggunakan seddan tidak ada file sementara yang terlihat :

Anda dapat menghindari membuat "file temp" yang terlihat terpisah :

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

Penjelasan

Sistem mirip Unix tidak benar-benar menghapus konten file dari disk sampai keduanya tidak terhubung dalam sistem file, dan tidak terbuka dalam proses apa pun. Jadi bisa Anda lakukan exec 3<untuk membuka file di shell untuk membaca file deskriptor 3, rmfile (yang memutuskan tautannya dari sistem file), dan kemudian memanggil seddengan deskriptor file 3 yang digunakan sebagai inputnya.

Perhatikan bahwa ini sangat berbeda dari ini:

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

Perbedaannya adalah ketika Anda melakukannya dalam satu perintah, shell hanya membuka file yang sama untuk membaca, dan untuk menulis dengan opsi untuk memotong file - karena masih file yang sama, Anda kehilangan konten. Tetapi jika Anda membukanya untuk dibaca, lalu rmmembuka pathname yang sama untuk menulis, Anda sebenarnya membuat file baru di pathname yang sama (tetapi di lokasi inode dan disk yang baru, karena aslinya masih terbuka): jadi isinya masih tersedia.

Kemudian setelah Anda selesai, Anda dapat menutup deskriptor file yang Anda buka sebelumnya (itulah yang dilakukan exec 3<&-sintaks khusus), yang melepaskan file asli sehingga sistem operasi dapat menghapus (menandai sebagai tidak digunakan) ruang disknya.

Peringatan

Ada beberapa hal yang perlu diingat tentang solusi ini :.

  1. Anda hanya mendapatkan satu "masuk" melalui konten - tidak ada cara portabel untuk shell untuk "mencari" kembali dalam deskriptor file - jadi setelah suatu program membaca beberapa konten, program lain hanya akan melihat sisa file. Dan sedakan membaca seluruh file.

  2. Ada kemungkinan kecil file asli Anda hilang jika shell / script / sed Anda terbunuh sebelum selesai.

mtraceur
sumber
Untuk lebih jelasnya, @TobySpeight sudah memposting contoh solusi ini di akhir jawabannya, tapi saya pikir itu bisa menggunakan elaborasi yang lebih mendalam.
mtraceur
Kepada para downvoter: Walaupun saya yakin hanya meninggalkan downvote membuat Anda merasa baik tentang diri Anda sendiri, saya akan menghargai jika Anda berkontribusi lebih banyak dengan memberikan umpan balik mengenai masalah apa yang Anda miliki dengan jawaban ini, bagaimana hal itu dapat ditingkatkan, dll. .
mtraceur
mungkin Anda bisa menggunakan perl -i
c4f4t0r
@ c4f4t0r: Maaf atas jawaban yang terlambat: Ya, perl -iadalah pilihan lain yang bagus. Pertanyaannya secara khusus meminta solusi kompatibel dengan Solaris' sed, berbeda dengan solusi dengan perldan awkbahwa mereka disebutkan sudah menemukan. Plus, jawaban saya terutama ditujukan untuk menambahkan sesuatu yang saya pikir berguna untuk diketahui dan hilang dari jawaban lain mana pun.
mtraceur