Saya perlu melakukan pencarian regex dan mengganti semua file dalam folder (dan subfoldernya). Apa perintah shell linux untuk melakukan itu?
Misalnya, saya ingin menjalankan ini di semua file dan menimpa file lama dengan teks baru yang diganti.
sed 's/old text/new text/g'
Jawaban:
Tidak ada cara untuk melakukannya hanya dengan menggunakan sed. Anda harus menggunakan setidaknya utilitas find bersama-sama:
find . -type f -exec sed -i.bak "s/foo/bar/g" {} \;
Perintah ini akan membuat
.bak
file untuk setiap file yang diubah.Catatan:
-i
argumen untuksed
perintah adalah ekstensi GNU, jadi, jika Anda menjalankan perintah ini dengan BSD yangsed
Anda akan perlu untuk mengarahkan output ke file baru kemudian mengubah nama itu.find
utilitas tidak melaksanakan-exec
argumen dalam kotak UNIX lama, sehingga, Anda akan perlu menggunakan| xargs
sebagai gantinya.sumber
\;
untuk?-i
dengan sendirinya tidak membuat file cadangan, dan itulah yang menyebabkan sed untuk melakukan operasi pada file di tempat.{}
untuk?{}
akan diganti dengan setiap nama file yang ditemukan olehfind
dan\;
memberitahu untuk menemukan bahwa perintah yang dia butuhkan untuk dieksekusi selesai pada titik ini.Saya lebih suka menggunakan
find | xargs cmd
overfind -exec
karena lebih mudah diingat.Contoh ini secara global menggantikan "foo" dengan "bar" dalam file .txt pada atau di bawah direktori Anda saat ini:
find . -type f -name "*.txt" -print0 | xargs -0 sed -i "s/foo/bar/g"
The
-print0
dan-0
pilihan dapat ditinggalkan jika nama file Anda tidak berisi karakter yang funky seperti spasi.sumber
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' "s/foo/bar/g"
(catatan memberikan string kosong ke-i
argumen).sed -i.bak
sebagai gantised -i
. Saya pikir seperti yang disebutkan oleh @JakubKukul,sed -i ''
juga berfungsi.Untuk portabilitas, saya tidak mengandalkan fitur sed yang khusus untuk linux atau BSD. Sebagai gantinya saya menggunakan
overwrite
skrip dari Kernighan dan buku Pike di Unix Programming Environment.Perintahnya kemudian
find /the/folder -type f -exec overwrite '{}' sed 's/old/new/g' {} ';'
Dan
overwrite
skrip (yang saya gunakan di semua tempat) adalah#!/bin/sh # overwrite: copy standard input to output after EOF # (final version) # set -x case $# in 0|1) echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2 esac file=$1; shift new=/tmp/$$.new; old=/tmp/$$.old trap 'rm -f $new; exit 1' 1 2 15 # clean up files if "$@" >$new # collect input then cp $file $old # save original file trap 'trap "" 1 2 15; cp $old $file # ignore signals rm -f $new $old; exit 1' 1 2 15 # during restore cp $new $file else echo "overwrite: $1 failed, $file unchanged" 1>&2 exit 1 fi rm -f $new $old
Idenya adalah bahwa itu menimpa file hanya jika perintah berhasil. Berguna di
find
dan juga di tempat yang tidak ingin Anda gunakansed 's/old/new/g' file > file # THIS CODE DOES NOT WORK
karena shell memotong file sebelum
sed
dapat membacanya.sumber
Mungkin saya sarankan (setelah membuat cadangan file Anda):
find /the/folder -type f -exec sed -ibak 's/old/new/g' {} ';'
sumber
Contoh: ganti {AutoStart} dengan 1 untuk semua file ini di bawah folder / app / config / dan folder anaknya:
sed 's/{AutoStart}/1/g' /app/config/**/*.ini
sumber
for i in $(ls);do sed -i 's/old_text/new_text/g' $i;done
sumber
Ini berfungsi untuk saya (di terminal mac, di Linux Anda tidak perlu
'' -e
):sed -i '' -e 's/old text/new text/g' `grep 'old text' -rl *`
perintah
grep 'old text' -rl *
mencantumkan semua file di direktori kerja (dan subdirektori) di mana "teks lama" ada. Ini kemudian diteruskan dalam sed.sumber
Mungkin ingin mencoba mencari / mengganti skrip Perl saya secara massal . Memiliki beberapa keunggulan dibandingkan solusi utilitas yang dirantai (seperti tidak harus berurusan dengan beberapa level interpretasi karakter metakarakter shell).
#!/usr/bin/perl use strict; use Fcntl qw( :DEFAULT :flock :seek ); use File::Spec; use IO::Handle; die "Usage: $0 startdir search replace\n" unless scalar @ARGV == 3; my $startdir = shift @ARGV || '.'; my $search = shift @ARGV or die "Search parameter cannot be empty.\n"; my $replace = shift @ARGV; $search = qr/\Q$search\E/o; my @stack; sub process_file($) { my $file = shift; my $fh = new IO::Handle; sysopen $fh, $file, O_RDONLY or die "Cannot read $file: $!\n"; my $found; while(my $line = <$fh>) { if($line =~ /$search/) { $found = 1; last; } } if($found) { print " Processing in $file\n"; seek $fh, 0, SEEK_SET; my @file = <$fh>; foreach my $line (@file) { $line =~ s/$search/$replace/g; } close $fh; sysopen $fh, $file, O_WRONLY | O_TRUNC or die "Cannot write $file: $!\n"; print $fh @file; } close $fh; } sub process_dir($) { my $dir = shift; my $dh = new IO::Handle; print "Entering $dir\n"; opendir $dh, $dir or die "Cannot open $dir: $!\n"; while(defined(my $cont = readdir($dh))) { next if $cont eq '.' || $cont eq '..'; # Skip .swap files next if $cont =~ /^\.swap\./o; my $fullpath = File::Spec->catfile($dir, $cont); if($cont =~ /$search/) { my $newcont = $cont; $newcont =~ s/$search/$replace/g; print " Renaming $cont to $newcont\n"; rename $fullpath, File::Spec->catfile($dir, $newcont); $cont = $newcont; $fullpath = File::Spec->catfile($dir, $cont); } if(-l $fullpath) { my $link = readlink($fullpath); if($link =~ /$search/) { my $newlink = $link; $newlink =~ s/$search/$replace/g; print " Relinking $cont from $link to $newlink\n"; unlink $fullpath; my $res = symlink($newlink, $fullpath); warn "Symlink of $newlink to $fullpath failed\n" unless $res; } } next unless -r $fullpath && -w $fullpath; if(-d $fullpath) { push @stack, $fullpath; } elsif(-f $fullpath) { process_file($fullpath); } } closedir($dh); } if(-f $startdir) { process_file($startdir); } elsif(-d $startdir) { @stack = ($startdir); while(scalar(@stack)) { process_dir(shift(@stack)); } } else { die "$startdir is not a file or directory\n"; }
sumber
Jika nama file dalam folder memiliki beberapa nama biasa (seperti file1, file2 ...) yang saya gunakan untuk siklus.
for i in {1..10000..100}; do sed 'old\new\g' 'file'$i.xml > 'cfile'$i.xml; done
sumber