Muszę znaleźć wyrażenie regularne i zamienić je na wszystkich plikach w folderze (i jego podfolderach). Jakie byłoby polecenie powłoki systemu Linux, aby to zrobić?
Na przykład chcę przeprowadzić to na wszystkich plikach i nadpisać stary plik nowym, zastąpionym tekstem.
sed 's/old text/new text/g'
Odpowiedzi:
Nie da się tego zrobić używając tylko seda. Będziesz musiał użyć przynajmniej narzędzia wyszukiwania razem:
find . -type f -exec sed -i.bak "s/foo/bar/g" {} \;
To polecenie utworzy
.bak
plik dla każdego zmienionego pliku.Uwagi:
-i
Argument zased
polecenie jest rozszerzeniem GNU, więc, jeśli używasz tego polecenia z BSD tosed
trzeba będzie przekierować wyjście do nowego pliku, a następnie zmień jego nazwę.find
Narzędzie nie wdrożyć-exec
argument w starych skrzynek UNIX, więc trzeba będzie użyć| xargs
zamiast.źródło
\;
?-i
sam w sobie nie tworzy pliku kopii zapasowej i to powoduje, że sed wykonuje operację na pliku w miejscu.{}
?{}
Zostaną zastąpione przez każdego pliku znalezionego przezfind
i\;
mówi się że komenda, że musi wykonać wykończenie w tym momencie.Wolę używać
find | xargs cmd
więcej,find -exec
ponieważ jest łatwiejszy do zapamiętania.Ten przykład globalnie zamienia „foo” na „bar” w plikach .txt w bieżącym katalogu lub poniżej:
find . -type f -name "*.txt" -print0 | xargs -0 sed -i "s/foo/bar/g"
-print0
I-0
opcje mogą być pominięte, jeśli nazwy plików nie zawierają znaki funkowe, takich jak przestrzenie.źródło
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' "s/foo/bar/g"
(pamiętaj o podaniu pustego ciągu do-i
argumentu).sed -i.bak
zamiastsed -i
. Myślę, że jak wspomniał @JakubKukul,sed -i ''
również działa.Jeśli chodzi o przenośność, nie polegam na funkcjach seda, które są specyficzne dla Linuksa lub BSD. Zamiast tego używam
overwrite
skryptu z książki Kernighan i Pike o środowisku programowania Unix.Rozkaz jest wtedy
find /the/folder -type f -exec overwrite '{}' sed 's/old/new/g' {} ';'
A
overwrite
skrypt (którego używam wszędzie) to#!/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
Chodzi o to, że nadpisuje plik tylko wtedy, gdy polecenie się powiedzie. Przydatne w,
find
a także tam, gdzie nie chcesz używaćsed 's/old/new/g' file > file # THIS CODE DOES NOT WORK
ponieważ powłoka obcina plik, zanim będzie
sed
mógł go odczytać.źródło
Mogę zasugerować (po utworzeniu kopii zapasowej plików):
find /the/folder -type f -exec sed -ibak 's/old/new/g' {} ';'
źródło
Przykład: zastąp {AutoStart} wartością 1 dla wszystkich plików ini w folderze / app / config / i jego folderach podrzędnych:
sed 's/{AutoStart}/1/g' /app/config/**/*.ini
źródło
for i in $(ls);do sed -i 's/old_text/new_text/g' $i;done
źródło
To zadziałało dla mnie (na terminalu Mac, w systemie Linux, którego nie potrzebujesz
'' -e
):sed -i '' -e 's/old text/new text/g' `grep 'old text' -rl *`
polecenie
grep 'old text' -rl *
wyświetla wszystkie pliki w katalogu roboczym (i podkatalogach), w których istnieje „stary tekst”. To jest następnie przekazywane w sed.źródło
Może zechcesz wypróbować mój masowy skrypt wyszukiwania / zamiany Perla . Ma pewne zalety w porównaniu z rozwiązaniami o charakterze łańcuchowym (np. Brak konieczności radzenia sobie z wieloma poziomami interpretacji metaznaków powłoki).
#!/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"; }
źródło
W przypadku, gdy nazwa plików w folderze ma jakieś zwykłe nazwy (takie jak plik1, plik2 ...) użyłem dla cyklu.
for i in {1..10000..100}; do sed 'old\new\g' 'file'$i.xml > 'cfile'$i.xml; done
źródło