Perintah sed dengan opsi -i gagal pada Mac, tetapi bekerja di Linux

303

Saya telah berhasil menggunakan sedperintah berikut untuk mencari / mengganti teks di Linux:

sed -i 's/old_link/new_link/g' *

Namun, ketika saya mencobanya di Mac OS X, saya mendapatkan:

"perintah c mengharapkan \ diikuti oleh teks"

Saya pikir Mac saya menjalankan shell BASH normal. Ada apa?

EDIT:

Menurut @ High Performance, ini karena Mac sedmemiliki rasa (BSD) yang berbeda, jadi pertanyaan saya adalah bagaimana cara mereplikasi perintah ini di BSD sed?

EDIT:

Berikut adalah contoh aktual yang menyebabkan ini:

sed -i 's/hello/gbye/g' *
Yarin
sumber
1
Ini berarti bahwa sedmelihat "c" di data Anda sebagai perintah. Apakah Anda menggunakan variabel? Silakan posting sesuatu yang lebih dekat mewakili perintah aktual dan beberapa data yang sedang Anda proses. Anda bisa mendapatkan demonstrasi sederhana dari kesalahan ini dengan melakukan echo x | sed c.
Dijeda sampai pemberitahuan lebih lanjut.
@ Dennis, perintah sederhana di atas menyebabkan ini, meskipun data yang diproses adalah seluruh situs web (saya mengonversi semua tautan gambar), termasuk file html dan css ...
Yarin

Jawaban:

397

Jika Anda menggunakan -iopsi ini, Anda perlu memberikan ekstensi untuk cadangan Anda.

Jika Anda memiliki:

File1.txt
File2.cfg

Perintah (perhatikan kurangnya ruang antara -idan ''dan -euntuk membuatnya bekerja pada versi baru dari Mac dan GNU):

sed -i'.original' -e 's/old_link/new_link/g' *

Buat 2 file cadangan seperti:

File1.txt.original
File2.cfg.original

Tidak ada cara portabel untuk menghindari membuat file cadangan karena tidak mungkin menemukan campuran perintah sed yang bekerja pada semua kasus:

  • sed -i -e ...- tidak berfungsi pada OS X karena membuat -ecadangan
  • sed -i'' -e ... - tidak bekerja pada OS X 10.6 tetapi bekerja pada 10.9+
  • sed -i '' -e ... - tidak bekerja pada GNU

Catatan Mengingat bahwa tidak ada perintah sed yang bekerja pada semua platform, Anda dapat mencoba menggunakan perintah lain untuk mencapai hasil yang sama.

Misalnya, perl -i -pe's/old_link/new_link/g' *

Sinetris
sumber
4
Saya memiliki masalah yang sama. Terima kasih atas solusi ini. Tapi ketika saya mencoba dengan 'man sed' untuk menemukan deskripsi '-i', tidak ada yang menggunakan -i '' untuk mengabaikan backup ada di sana. Ini adalah kesalahan pertama saya. Kedua, ketika kesalahan "perintah mengharapkan \ diikuti oleh teks" muncul, mengapa tidak langsung memberi tahu kami bahwa ia mengharapkan nama cadangan untuk opsi '-i' !! ?? Hal seperti itu terjadi di mana-mana: Anda mendapatkan kesalahan tetapi bukan mengapa kesalahan itu, lalu Anda mencari manual yang tidak menjelaskan apa pun tentangnya. Maka Anda google itu untuk menemukan orang lain juga memiliki masalah yang sama. Maksud saya, mengapa tidak memberi contoh dalam manual?
lukmac
atau beri tahu kami mengapa ada kesalahan alih-alih hanya pesan tanpa informasi yang terjadi kesalahan. Ini adalah saran untuk semua pembuat alat, jika mereka dapat membaca komentar ini.
lukmac
5
@ lukmac Sejauh yang sedbisa Anda ketahui, Anda TIDAK menyediakan akhiran cadangan. Sufiks cadangan adalah s/old_link/new_link/g. Argumen selanjutnya setelah itu seharusnya adalah perintah pengeditan. Karena itu mengartikan perintah sebagai nama cadangan, ia kemudian mengambil nama file pertama sebagai perintah pengeditan, tetapi mereka tidak valid.
Barmar
2
Apakah ini akan selalu menjadi masalah? Apakah ada beberapa cara Apple mungkin dapat membuat solusi / paket GNU dengan OSX? Atau, bukankah GNU bisa mendukung sed -i '' -e ...?
blong
5
sed -i'' -etampaknya tidak berfungsi seperti yang diharapkan pada mac 10.14
MoOx
64

Saya percaya pada OS X ketika Anda menggunakan -i ekstensi untuk file cadangan diperlukan . Mencoba:

sed -i .bak 's/hello/gbye/g' *

Menggunakan GNU sed, ekstensi bersifat opsional .

Dijeda sampai pemberitahuan lebih lanjut.
sumber
55

Ini berfungsi dengan versi sed GNU dan BSD:

sed -i'' -e 's/old_link/new_link/g' *

atau dengan cadangan:

sed -i'.bak' -e 's/old_link/new_link/g' *

Catat ruang yang hilang setelah -iopsi! (Diperlukan untuk GNU sed)

chipiik
sumber
7
Yang pertama tidak berfungsi pada OSX (Saya baru saja mengujinya pada 10.6.8)
marcin
4
Bagi saya di OS X (10.10.3), yang pertama membuat file cadangan dengan su. Tidak baik. Yang kedua adalah satu-satunya yang bekerja untuk saya secara konsisten antara Ubuntu dan OS X. Namun saya tidak ingin file cadangan, jadi saya harus menjalankan rmperintah setelah itu untuk menghapusnya.
Brendan
1
Baris pertama harus ditulis ulang dengan spasi untuk bekerja pada 10.10: sed -i'' ...=>sed -i '' ...
Daniel Jomphe
3
@DanielJomphe Tetapi menambahkan ruang ini tidak berfungsi pada GNU sed
Brice
1
@marcin yang pertama berfungsi untuk saya juga di OSX 10.11.2. Satu-satunya hal adalah membuat file cadangan, yang perlu dihapus setelahnya. Kedua terlihat lebih baik, karena kita tidak harus mencari tahu nama file nanti.
vikas027
41

Punya masalah yang sama di Mac dan menyelesaikannya dengan brew:

brew install gnu-sed

dan gunakan sebagai

gsed SED_COMMAND

Anda dapat mengatur juga mengatur sedsebagai alias untuk gsed(jika Anda mau):

alias sed=gsed
Sruthi Poddutur
sumber
3
Mengapa Anda memberikan jawaban yang persis sama dengan Ohad Kravchick ?
gniourf_gniourf
1
pengaturan alias seperti ini bukan ide bagus
SantaXL
1
Alih-alih alias, seperti yang direkomendasikan dalam halaman pembuatan yang sesuai, tambahkan ke path: PATH = "$ (brew --prefix) / opt / gnu-sed / libexec / gnubin: $ PATH"
y.luis
24

Atau, Anda dapat menginstal versi sed GNU di Mac Anda, yang disebut gsed, dan menggunakannya menggunakan sintaks Linux standar.

Untuk itu, instal gsedmenggunakan port (jika Anda tidak memilikinya, dapatkan di http://www.macports.org/ ) dengan menjalankan sudo port install gsed. Lalu, Anda bisa berlarised -i 's/old_link/new_link/g' *

Ohad Kravchick
sumber
24
.. atau jika Anda menggunakan homebrew, maka instalgnu-sed
Sudar
1
Terima kasih @Sudar, jempol!
Andrew Swan
9

Mac Anda memang menjalankan BASH shell, tetapi ini lebih merupakan pertanyaan implementasi sed yang Anda hadapi. Pada Mac, sed berasal dari BSD dan sedikit berbeda dari sed yang Anda temukan pada kotak Linux yang khas. Saya sarankan kamu man sed.

Tanda Kinerja Tinggi
sumber
3
Terima kasih telah menunjukkan masalah BSD- Tapi saya cukup buta huruf dan hanya perlu perbaikan cepat untuk perintah saya - pandangan sekilas pada pria tidak mengatakan apa
Yarin
5
@Yarin - tidak, dan jika saya melihat sekilas pada pria, itu juga tidak memberitahu Anda apa-apa. Coba lihat lebih lama.
High Performance Mark
21
Sebagian besar jawaban SO dikubur di suatu tempat dalam diri seorang pria, tetapi itulah yang dilakukan SO untuk orang-orang sibuk yang membutuhkan jawaban dari orang
Yarin
9

Jawaban Sinetris benar, tetapi saya menggunakan ini dengan findperintah untuk lebih spesifik tentang file apa yang ingin saya ubah. Secara umum ini harus bekerja (diuji pada OSX /bin/bash):

find . -name "*.smth" -exec sed -i '' 's/text1/text2/g' {} \;

Secara umum ketika menggunakan sedtanpa finddalam proyek yang kompleks kurang efisien.

Lucas
sumber
-execsangat baik! Saya hanya ingin tahu apakah tebasan pada akhirnya benar-benar dibutuhkan
Ye Liu
2
@YeLiu: tanpa \, ;ditafsirkan oleh shell ketika saya mencoba seperti itu
serv-inc
2

Saya telah membuat fungsi untuk menangani sedperbedaan antara MacOS (diuji pada MacOS 10.12) dan OS lainnya:

OS=`uname`
# $(replace_in_file pattern file)
function replace_in_file() {
    if [ "$OS" = 'Darwin' ]; then
        # for MacOS
        sed -i '' -e "$1" "$2"
    else
        # for Linux and Windows
        sed -i'' -e "$1" "$2"
    fi
}

Pemakaian:

$(replace_in_file 's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' "./mysql/.env")

Dimana:

, adalah delimeter

's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' adalah pola

"./mysql/.env" adalah path ke file

v.babak
sumber
1

Inilah cara menerapkan variabel lingkungan ke file templat (tidak perlu cadangan).

1. Buat template dengan {{FOO}} untuk diganti nanti.

echo "Hello {{FOO}}" > foo.conf.tmpl

2. Ganti {{FOO}} dengan variabel FOO dan hasilkan ke file foo.conf baru

FOO="world" && sed -e "s/{{FOO}}/$FOO/g" foo.conf.tmpl > foo.conf

Bekerja baik macOS 10.12.4 dan Ubuntu 14.04.5

katopz
sumber
0

Seperti yang ditunjukkan oleh jawaban lain, tidak ada cara untuk menggunakan portable di OS X dan Linux tanpa membuat file cadangan. Jadi, saya malah menggunakan Ruby satu-liner ini untuk melakukannya:

ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml

Dalam kasus saya, saya perlu menyebutnya dari raketugas (yaitu, di dalam skrip Ruby), jadi saya menggunakan tingkat kutipan tambahan:

sh %q{ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml}
Dan Kohn
sumber
-1
sed -ie 's/old_link/new_link/g' *

Bekerja pada BSD & Linux

ashokrajar
sumber
3
Ini menciptakan di linux nama file lain dengan emenambahkan
Brice
1
Rusak, lebih baik untuk menghapusnya.
Sorin
Ini bekerja di Mac dan Linux. Apa masalah dengan solusi ini?
Islam Azab
Tidak, ini tidak berfungsi pada Mac BSD Sed - ini akan membuat file cadangan dengan 'e' ditambahkan ke nama file.
Jesper Grann Laursen