Mengganti string dalam file berdasarkan kriteria pencarian tertentu adalah tugas yang sangat umum. Bagaimana bisa saya
- ganti string
foo
denganbar
di semua file di direktori saat ini? - melakukan hal yang sama secara rekursif untuk sub direktori?
- ganti hanya jika nama file cocok dengan string lain?
- ganti hanya jika string ditemukan dalam konteks tertentu?
- ganti jika string ada pada nomor baris tertentu?
- ganti beberapa string dengan penggantian yang sama
- ganti beberapa string dengan penggantian yang berbeda
text-processing
awk
sed
perl
terdon
sumber
sumber
Jawaban:
1. Mengganti semua kemunculan satu string dengan yang lain di semua file di direktori saat ini:
Ini untuk kasus-kasus di mana Anda tahu bahwa direktori tersebut hanya berisi file biasa dan Anda ingin memproses semua file yang tidak disembunyikan. Jika bukan itu masalahnya, gunakan pendekatan dalam 2.
Semua
sed
solusi dalam jawaban ini menganggap GNUsed
. Jika menggunakan FreeBSD atau OS / X, ganti-i
dengan-i ''
. Perhatikan juga bahwa penggunaan-i
switch dengan versi apa punsed
memiliki implikasi keamanan sistem file tertentu dan tidak disarankan dalam skrip apa pun yang Anda rencanakan untuk didistribusikan dengan cara apa pun.Non rekursif, hanya file dalam direktori ini:
(yang
perl
akan gagal untuk nama file yang diakhiri|
atau spasi) ).File rekursif dan teratur ( termasuk yang tersembunyi ) di ini dan semua subdirektori
Jika Anda menggunakan zsh:
(mungkin gagal jika daftar terlalu besar, lihat
zargs
untuk bekerja di sekitar).Bash tidak dapat memeriksa langsung untuk file biasa, diperlukan loop (kawat gigi menghindari pengaturan opsi secara global):
File-file dipilih ketika mereka adalah file aktual (-f) dan mereka dapat ditulis (-w).
2. Ganti hanya jika nama file cocok dengan string lain / memiliki ekstensi spesifik / jenis tertentu dll:
Non-rekursif, hanya file dalam direktori ini:
File rekursif dan teratur dalam subdirektori ini dan semua
Jika Anda menggunakan bash (kawat gigi hindari pengaturan opsi secara global):
Jika Anda menggunakan zsh:
The
--
servis untuk memberitahused
bahwa tidak ada lagi bendera akan diberikan pada command line. Ini berguna untuk melindungi terhadap nama file yang dimulai dengan-
.Jika suatu file berjenis tertentu, misalnya, dapat dieksekusi (lihat
man find
opsi lainnya):zsh
:3. Ganti hanya jika string ditemukan dalam konteks tertentu
Ganti
foo
denganbar
hanya jika adabaz
kemudian pada baris yang sama:Di
sed
, menggunakan\( \)
save apa pun yang ada di dalam tanda kurung dan Anda kemudian dapat mengaksesnya\1
. Ada banyak variasi tema ini, untuk mempelajari lebih lanjut tentang ekspresi reguler seperti itu, lihat di sini .Ganti
foo
denganbar
hanya jikafoo
ditemukan pada kolom 3d (lapangan) dari file input (dengan asumsi bidang spasi-terpisah):(perlu
gawk
4.1.0 atau lebih baru).Untuk bidang yang berbeda cukup gunakan di
$N
manaN
jumlah bidang yang diminati. Untuk pemisah bidang yang berbeda (:
dalam contoh ini) gunakan:Solusi lain menggunakan
perl
:CATATAN: keduanya
awk
danperl
solusi akan memengaruhi penspasian dalam file (menghapus blanko terkemuka dan tertinggal, dan mengonversi sekuens blank menjadi satu karakter spasi pada baris yang cocok). Untuk bidang yang berbeda, gunakan di$F[N-1]
manaN
nomor bidang yang Anda inginkan dan untuk penggunaan pemisah bidang yang berbeda ($"=":"
set pemisah bidang keluaran untuk:
):Ganti
foo
denganbar
hanya pada baris ke-4:4. Beberapa operasi penggantian: ganti dengan string yang berbeda
Anda dapat menggabungkan
sed
perintah:Sadarilah bahwa pesanan itu penting (
sed 's/foo/bar/g; s/bar/baz/g'
akan digantifoo
denganbaz
).atau perintah Perl
Jika Anda memiliki sejumlah besar pola, lebih mudah untuk menyimpan pola dan penggantiannya dalam
sed
file skrip:Atau, jika Anda memiliki terlalu banyak pasangan pola untuk dapat di atas, Anda dapat membaca pasangan pola dari file (dua pola yang dipisahkan spasi, $ pola dan $ penggantian, per baris):
Itu akan sangat lambat untuk daftar panjang pola dan file data besar sehingga Anda mungkin ingin membaca pola dan membuat
sed
skrip dari mereka. Pembatas berikut mengasumsikan pembatas <spasi> memisahkan daftar MATCH <spasi> REPLACE pasang yang terjadi satu per baris dalam filepatterns.txt
:Format di atas sebagian besar arbitrer dan, misalnya, tidak memungkinkan untuk <spasi> dalam MATCH atau REPLACE . Metode ini sangat umum: pada dasarnya, jika Anda dapat membuat aliran keluaran yang terlihat seperti
sed
skrip, maka Anda dapat sumber aliran tersebut sebagaised
skrip dengan menetapkansed
file skrip sebagai-
stdin.Anda dapat menggabungkan dan menggabungkan beberapa skrip dengan cara yang sama:
POSIX
sed
akan menggabungkan semua skrip menjadi satu sesuai dengan urutannya pada baris perintah. Tak satu pun dari ini perlu diakhiri dengan\n
ewline.grep
dapat bekerja dengan cara yang sama:Ketika bekerja dengan string-tetap sebagai pola, adalah praktik yang baik untuk melepaskan karakter metaforeks reguler . Anda dapat melakukan ini dengan agak mudah:
5. Operasi penggantian berganda: ganti beberapa pola dengan string yang sama
Ganti salah satu dari
foo
,bar
ataubaz
denganfoobar
atau
sumber
zsh
. Dengan segala cara menambahkanzsh
info tetapi tidak ada alasan untuk menghapus hal-hal bash. Juga, saya tahu bahwa menggunakan shell untuk pemrosesan teks tidak ideal tetapi ada beberapa kasus di mana dibutuhkan. Saya mengedit dalam versi yang lebih baik dari skrip asli saya yang akan membuatsed
skrip daripada benar-benar menggunakan shell loop untuk menguraikan. Ini dapat bermanfaat jika Anda memiliki beberapa ratus pasang pola misalnya.(.)
kualifikasi globbing sehingga tidak dapat digunakan di sini. (Anda melewatkan beberapa - juga). Untuk loop tidak benar (hilang -r) dan berarti membuat beberapa lintasan dalam file dan tidak menambah manfaat atas skrip sed.--
setelahsed -i
dan sebelum perintah pengganti?-
. Menggunakannya memastikan bahwa perintah akan bekerja pada file dengan nama seperti-foo
. Tanpa itu,-f
akan diurai sebagai opsi..git
direktori, dan benar-benar mengacaukan checkout Anda. Lebih baik beroperasi dalam / pada direktori tertentu dengan nama.A good r e pl acement alat Linux adalah RPL , yang pada awalnya ditulis untuk proyek Debian, sehingga tersedia dengan
apt-get install rpl
dalam Debian berasal distro, dan mungkin bagi orang lain, tapi jika tidak Anda dapat men-downloadtar.gz
file dalam SourgeForge .Contoh penggunaan paling sederhana:
Perhatikan bahwa jika string berisi spasi, ia harus dilampirkan dalam tanda kutip. Secara default
rpl
mengurus huruf kapital tetapi bukan dari kata-kata lengkap , tetapi Anda dapat mengubah default ini dengan pilihan-i
(mengabaikan kasus) dan-w
(seluruh kata-kata). Anda juga dapat menentukan beberapa file :Atau bahkan menentukan ekstensi (
-x
) untuk mencari atau bahkan mencari secara rekursif (-R
) di direktori:Anda juga dapat mencari / mengganti dalam mode interaktif dengan
-p
opsi (cepat):Outputnya menunjukkan jumlah file / string yang diganti dan jenis pencarian (huruf dalam / sensitif, seluruh / sebagian kata), tetapi bisa diam dengan opsi
-q
( mode senyap ), atau bahkan lebih verbose, daftar nomor baris yang berisi cocok dengan setiap file dan direktori dengan opsi-v
( mode verbose ).Opsi lain yang patut diingat adalah
-e
(honor e scapes) yang memungkinkanregular expressions
, sehingga Anda juga dapat mencari tabs (\t
), baris baru (\n
), dll. Bahkan Anda dapat menggunakan-f
untuk memaksa izin (tentu saja, hanya ketika pengguna memiliki izin menulis) dan-d
untuk menjaga waktu modifikasi`).Akhirnya, jika Anda tidak yakin akan membuat yang tepat, gunakan
-s
( mode simulasi ).sumber
Cara melakukan pencarian dan mengganti lebih dari beberapa file menyarankan:
Hasil terbaik saya berasal dari menggunakan perl dan grep (untuk memastikan file memiliki ekspresi pencarian)
sumber
Anda dapat menggunakan Vim dalam mode Ex:
sumber
Saya menggunakan ini:
Daftar semua file yang berisi
old_string
.Ganti baris baru dalam hasil dengan spasi (sehingga daftar file dapat diumpankan ke
sed
.Jalankan
sed
pada file-file itu untuk mengganti string lama dengan yang baru.Pembaruan: Hasil di atas akan gagal pada nama file yang mengandung spasi putih. Sebaliknya, gunakan:
grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'
sumber
grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'
akan membuatnya berurusan dengan nama file yang sewenang-wenang.Dari perspektif pengguna, alat Unix yang bagus & sederhana yang melakukan pekerjaan dengan sempurna
qsubst
. Sebagai contoh,akan diganti
foo
denganbar
di semua file C saya. Fitur yang bagus adalah yangqsubst
akan melakukan query-replace , yaitu, ia akan menunjukkan kepada saya setiap kemunculanfoo
dan bertanya apakah saya ingin menggantinya atau tidak. [Anda dapat mengganti tanpa syarat (tanpa bertanya) dengan-go
opsi, dan ada opsi lain, misalnya,-w
jika Anda hanya ingin menggantifoo
ketika itu merupakan keseluruhan kata.]Cara mendapatkannya:
qsubst
ditemukan oleh der Mouse (dari McGill) dan diposting ke comp.unix.sources 11 (7) pada Agustus 1987. Versi yang diperbarui ada. Sebagai contoh, versi NetBSDqsubst.c,v 1.8 2004/11/01
mengkompilasi dan berjalan dengan sempurna di mac saya.sumber
Saya membutuhkan sesuatu yang akan memberikan opsi lari-kering dan akan bekerja secara rekursif dengan bola, dan setelah mencoba melakukannya dengan
awk
dansed
saya menyerah dan bukannya melakukannya dengan python.The Script mencari rekursif semua file yang cocok dengan pola glob (misalnya
--glob="*.html"
) untuk regex dan menggantikan dengan regex pengganti:Setiap opsi panjang seperti
--search-regex
memiliki opsi pendek yang sesuai, yaitu-s
. Jalankan dengan-h
untuk melihat semua opsi.Misalnya, ini akan membalik semua tanggal dari
2017-12-31
ke31-12-2017
:sumber
globstar
opsi bash (atau yang setara dengan shell Anda) dan**
gumpalan ataufind
. Untuk lari kering, gunakan sajased
. Kecuali Anda menggunakan-i
opsi, itu tidak akan membuat perubahan. Untuk penggunaan cadangansed -i.bak
(atauperl -i .bak
); untuk file yang tidak cocok, gunakangrep PATTERN file || echo file
. Dan mengapa di dunia Anda ingin python memperluas gumpalan alih-alih membiarkan shell melakukannya? Kenapascript.py --glob=foo*
bukannya adilscript.py foo*
?sed
danawk
sehat dan tidak mau menginvestasikan waktu ekstra untuk menguasainya, (4) keterbacaan, (5) solusi ini juga akan bekerja pada sistem non-posix (Bukan karena aku butuh itu tapi orang lain mungkin).ripgrep (nama perintah
rg
) adalahgrep
alat, tetapi mendukung pencarian dan ganti juga.rg
tidak mendukung opsi di tempat, jadi Anda harus melakukannya sendiriLihat dokumentasi Rust regex untuk sintaks dan fitur ekspresi reguler. The
-P
switch akan memungkinkan PCRE2 rasa.rg
mendukung Unicode secara default.Seperti
grep
,-F
opsi akan memungkinkan string tetap untuk dicocokkan, opsi praktis yang saya rasased
harus diterapkan juga.Opsi praktis lainnya adalah
-U
yang memungkinkan pencocokan multilinerg
dapat menangani file dos-style jugaKeuntungan lain
rg
adalah kemungkinan lebih cepat daripadased
sumber