Mengganti string dalam file berdasarkan kriteria pencarian tertentu adalah tugas yang sangat umum. Bagaimana saya bisa
- ganti string
foo
denganbar
di semua file di direktori saat ini? - lakukan 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 berada pada nomor baris tertentu?
- ganti beberapa string dengan pengganti yang sama
- ganti beberapa string dengan pengganti yang berbeda
Jawaban yang Diterima:
1. Mengganti semua kemunculan satu string dengan yang lain di semua file di direktori saat ini:
Ini untuk kasus di mana Anda tahu bahwa direktori hanya berisi file biasa dan Anda ingin memproses semua file yang tidak disembunyikan. Jika bukan itu masalahnya, gunakan pendekatan di 2.
Semua sed
solusi dalam jawaban ini menganggap GNU sed
. Jika menggunakan FreeBSD atau macOS, ganti -i
dengan -i ''
. Perhatikan juga bahwa penggunaan -i
beralih dengan versi sed
memiliki implikasi keamanan sistem file tertentu dan tidak disarankan dalam skrip apa pun yang Anda rencanakan untuk didistribusikan dengan cara apa pun.
-
Non rekursif, file dalam direktori ini saja:
sed -i -- 's/foo/bar/g' * perl -i -pe 's/foo/bar/g' ./*
(perl
satu akan gagal untuk nama file yang diakhiri dengan |
atau spasi)).
-
File reguler dan rekursif (termasuk yang tersembunyi ) di ini dan semua subdirektori
find . -type f -exec sed -i 's/foo/bar/g' {} +
Jika Anda menggunakan zsh:
sed -i -- 's/foo/bar/g' **/*(D.)
(mungkin gagal jika daftar terlalu besar, lihat
zargs
untuk bekerja di sekitar).Bash tidak dapat memeriksa file biasa secara langsung, diperlukan loop (kawat gigi menghindari pengaturan opsi secara global):
( shopt -s globstar dotglob; for file in **; do if [[ -f $file ]] && [[ -w $file ]]; then sed -i -- 's/foo/bar/g' "$file" fi done )
File-file tersebut dipilih ketika mereka adalah file yang sebenarnya (-f) dan mereka dapat ditulis (-w).
2. Ganti hanya jika nama file cocok dengan string lain / memiliki ekstensi tertentu / dari jenis tertentu dll:
-
Non-rekursif, file dalam direktori ini saja:
sed -i -- 's/foo/bar/g' *baz* ## all files whose name contains baz sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz
-
File reguler dan rekursif di subdirektori ini dan semua
find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +
Jika Anda menggunakan bash (kawat gigi menghindari pengaturan opsi secara global):
( shopt -s globstar dotglob sed -i -- 's/foo/bar/g' **baz* sed -i -- 's/foo/bar/g' **.baz )
Jika Anda menggunakan zsh:
sed -i -- 's/foo/bar/g' **/*baz*(D.) sed -i -- 's/foo/bar/g' **/*.baz(D.)
--
berfungsi untuk memberitahu sed
bahwa tidak ada lagi flag yang akan diberikan di baris perintah. Ini berguna untuk melindungi dari nama file yang dimulai dengan -
.
-
Jika file bertipe tertentu, misalnya, dapat dieksekusi (lihat
man find
untuk opsi lainnya):find . -type f -executable -exec sed -i 's/foo/bar/g' {} +
zsh
:
sed -i -- 's/foo/bar/g' **/*(D*)
3. Ganti hanya jika string ditemukan dalam konteks tertentu
-
Ganti
foo
denganbar
hanya jika adabaz
nanti di baris yang sama:sed -i 's/foo(.*baz)/bar1/' file
Di sed
, menggunakan ( )
menyimpan apa pun yang ada di dalam tanda kurung dan Anda kemudian dapat mengaksesnya dengan 1
. Ada banyak variasi dari tema ini, untuk mempelajari lebih lanjut tentang ekspresi reguler tersebut, lihat di sini.
-
Ganti
foo
denganbar
hanya jikafoo
ditemukan pada kolom 3d (bidang) dari file input (dengan asumsi bidang yang dipisahkan spasi):gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file
(perlu gawk
4.1.0 atau lebih baru).
-
Untuk bidang yang berbeda cukup gunakan
$N
dimanaN
adalah jumlah bidang yang diminati. Untuk pemisah bidang yang berbeda (:
dalam contoh ini) gunakan:gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file
Solusi lain menggunakan perl
:
perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo
CATATAN:keduanya awk
dan perl
solusi akan mempengaruhi spasi dalam file (hapus awal dan akhir kosong, dan ubah urutan kosong menjadi satu karakter spasi di baris yang cocok). Untuk bidang yang berbeda, gunakan $F[N-1]
dimana N
adalah nomor bidang yang Anda inginkan dan untuk penggunaan pemisah bidang yang berbeda ($"=":"
menyetel pemisah bidang keluaran ke :
):
perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo
-
Ganti
foo
denganbar
hanya di baris ke-4:sed -i '4s/foo/bar/g' file gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file perl -i -pe 's/foo/bar/g if $.==4' file
4. Beberapa operasi penggantian:ganti dengan string yang berbeda
-
Anda dapat menggabungkan
sed
perintah:sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
Ketahuilah bahwa pesanan itu penting (sed 's/foo/bar/g; s/bar/baz/g'
akan menggantikan foo
dengan baz
).
-
atau perintah Perl
perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
-
Jika Anda memiliki banyak pola, akan lebih mudah untuk menyimpan pola Anda dan penggantinya dalam
sed
file skrip:#! /usr/bin/sed -f s/foo/bar/g s/baz/zab/g
-
Atau, jika Anda memiliki terlalu banyak pasangan pola agar hal di atas dapat dilakukan, Anda dapat membaca pasangan pola dari sebuah file (dua pola yang dipisahkan spasi, $pattern dan $replacement, per baris):
while read -r pattern replacement; do sed -i "s/$pattern/$replacement/" file done < patterns.txt
-
Itu akan sangat lambat untuk daftar panjang pola dan file data besar sehingga Anda mungkin ingin membaca pola dan membuat
sed
skrip dari mereka sebagai gantinya. Berikut ini mengasumsikan <spasi> pembatas memisahkan daftar MATCH<spasi>REPLACE pasangan terjadi satu per baris dalam filepatterns.txt
:sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt | sed -f- ./editfile >outfile
Format di atas sebagian besar bersifat arbitrer dan, misalnya, tidak mengizinkan <spasi> di salah satu dari MATCH atau GANTI . Metode ini sangat umum:pada dasarnya, jika Anda dapat membuat aliran keluaran yang terlihat seperti sed
script, maka Anda dapat sumber aliran itu sebagai sed
skrip dengan menentukan sed
file skrip sebagai -
stdin.
-
Anda dapat menggabungkan dan menggabungkan beberapa skrip dengan cara yang sama:
SOME_PIPELINE | sed -e'#some expression script' -f./script_file -f- -e'#more inline expressions' ./actual_edit_file >./outfile
Sebuah POSIX sed
akan menggabungkan semua skrip menjadi satu dalam urutan yang muncul di baris perintah. Tak satu pun dari kebutuhan ini diakhiri dengan n
baris baru.
-
grep
dapat bekerja dengan cara yang sama:sed -e'#generate a pattern list' <in | grep -f- ./grepped_file
-
Saat bekerja dengan string tetap sebagai pola, adalah praktik yang baik untuk menghindari ekspresi reguler metacharacters . Anda dapat melakukannya dengan lebih mudah:
sed 's/[]$&^*./[]/\&/g s| *([^ ]*) *([^ ]*).*|s/1/2/g| ' <patterns.txt | sed -f- ./editfile >outfile
5. Operasi penggantian ganda:ganti beberapa pola dengan string yang sama
-
Ganti salah satu
foo
,bar
ataubaz
denganfoobar
sed -Ei 's/foo|bar|baz/foobar/g' file
-
atau
perl -i -pe 's/foo|bar|baz/foobar/g' file