Menggunakan sed dan grep / egrep untuk mencari dan mengganti

97

Saya menggunakan egrep -Rdiikuti oleh ekspresi reguler yang berisi sekitar 10 serikat, jadi seperti: .jpg | .png | .gifdll. Ini berfungsi dengan baik, sekarang saya ingin mengganti semua string yang ditemukan dengan.bmp

Saya sedang memikirkan sesuatu seperti

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

jadi masalahnya di sini adalah bagaimana saya melakukan ekspresi reguler gabungan yang serupa seddan bagaimana cara memberitahukannya untuk menyimpan perubahan ke file yang mendapat masukan darinya.

Ori
sumber
2
Saya menemukan pertanyaan ini saat mencari cara untuk mencari dan mengganti beberapa file dalam hierarki direktori. Untuk orang lain dalam situasi saya, coba rpl .
titaniumdecoy
terima kasih rpl bekerja dan sangat mudah diingat .. hanya rpl old_string new_string target_files.
cesarpachon

Jawaban:

183

Gunakan perintah ini:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: temukan garis yang cocok menggunakan ekspresi reguler yang diperluas

    • -l: hanya mencantumkan nama file yang cocok

    • -R: mencari secara rekursif melalui semua direktori yang diberikan

    • -Z: gunakan \0sebagai pemisah record

    • "\.jpg|\.png|\.gif": cocok dengan salah satu string ".jpg", ".gif"atau".png"

    • .: memulai pencarian di direktori saat ini

  • xargs: jalankan perintah dengan stdin sebagai argumen

    • -0: gunakan \0sebagai pemisah record. Ini penting untuk mencocokkan -Zdari egrepdan untuk menghindari tertipu oleh spasi dan baris baru dalam nama file masukan.

    • -l: gunakan satu baris per perintah sebagai parameter

  • sed: Yang s tream ed itor

    • -i: mengganti file input dengan output tanpa membuat cadangan

    • -e: gunakan argumen berikut sebagai ekspresi

    • 's/\.jpg\|\.gif\|\.png/.bmp/g': mengganti semua kemunculan string ".jpg", ".gif"atau ".png"dengan".bmp"

David Schmitt
sumber
semuanya bekerja kecuali | di bagian sed. Saya tidak mengerti mengapa karena itu masuk akal ... bagian -l dari xargs memberi saya kesalahan jadi saya mengeluarkannya, mungkinkah itu terkait?
Ori
Saya menemukan bahwa perintah ini menambahkan baris baru ke akhir semua file yang diprosesnya.
titaniumdecoy
@titanumdecoy: Saya tidak dapat meniru perilaku ini. versi sed apa yang Anda gunakan dan di OS apa Anda?
David Schmitt
1
@DavidSchmitt: Anda mungkin ingin menggunakan sed -rekspresi reguler yang diperluas. Pada titik itu, pola akan cocok dengan yang digunakan di egrep, dan Anda mungkin ingin memasukkannya ke dalam variabel untuk digunakan kembali.
bukzor
Perintah ini menghemat waktu berjam-jam untuk menyalin file header dari aplikasi saya untuk perpustakaan yang saya buat. Ini luar biasa :) Ini adalah perintah yang saya gunakan egrep -lRZ "\ .h $". | xargs -0 tar -cvf headers.tar | (cp headers.tar header; cd headers; tar xf headers.tar;)
The Lazy Coder
11

Sejujurnya, saya suka sed untuk tugas yang sesuai, ini jelas merupakan tugas untuk perl - ini benar-benar lebih kuat untuk jenis satu baris ini, terutama untuk "menuliskannya kembali ke tempat asalnya" ( -isakelar perl melakukannya untuk Anda, dan secara opsional juga memungkinkan Anda menyimpan versi lama misalnya dengan menambahkan .bak, gunakan -i.baksaja).

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

daripada pekerjaan yang rumit di sed (jika mungkin ada) atau awk ...

Alex Martelli
sumber
6
sed menggunakan -i, seperti perl.
Stobor
@Stobor - Saya bersumpah saya mengalami masalah di mana operasi perl ketika saya memberi makan string pengganti regex melakukan persis apa yang saya inginkan, tidak seperti sed, bahkan jika saya memberikan opsi regex ke sed.. Saya rasa saya lupa beberapa flag ke sed atau itu memiliki keterbatasan.
meder omuraliev
10

Cara lain untuk melakukan ini

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

Beberapa bantuan terkait perintah di atas

Find akan melakukan pencarian untuk Anda pada direktori saat ini yang ditunjukkan oleh .

-name nama file dalam kasus saya pom.xml-nya dapat memberikan kartu liar.

-exec menjalankan

sed editor aliran

-i abaikan kasus

s adalah untuk pengganti

/4.6.0.../ String yang akan dicari

/5.0.0.../ String yang akan diganti

Cyril Cherian
sumber
mungkin tidak sekuat itu - tapi ini jauh lebih mudah bagi saya untuk memahaminya daripada jawaban yang diterima
icc97
5

Saya tidak bisa mendapatkan perintah apa pun di halaman ini untuk bekerja untuk saya: solusi sed menambahkan baris baru ke akhir semua file yang diproses, dan solusi perl tidak dapat menerima cukup argumen dari find. Saya menemukan solusi ini yang berfungsi dengan sempurna:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

Ini akan berulang ke bawah pohon direktori saat ini dan mengganti search_regexdengan replacement_stringdi file apa pun yang diakhiri dengan .hatau .m.

Saya juga pernah menggunakan rpl untuk tujuan ini di masa lalu.

titaniumdecoy
sumber
0

coba sesuatu menggunakan for loop

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

tidak cantik, tapi harus dilakukan.

Don Johe
sumber
3
xargs pasti lebih cocok di sini.
Nathan Fellman
1
dan menggunakan |while read ipola akan memungkinkan streaming dan menghindari pembatasan panjang saat hasil egrep menjadi terlalu panjang
David Schmitt
0

Kasus penggunaan saya adalah saya ingin mengganti foo:/Drive_Letterdengan foo:/bar/baz/xyz Dalam kasus saya, saya dapat melakukannya dengan kode berikut. Saya berada di lokasi direktori yang sama di mana ada banyak file.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

berharap itu membantu.

PEMBARUAN s | foo: / Drive_letter: | foo: / ba / baz / xyz | g

neo7
sumber
Anda dapat menggunakan pembatas lain untuk perintah sed, dan hal itu membuat nama jalur jauh lebih baik:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'
Kevin