Мне нужно найти и заменить регулярное выражение для всех файлов в папке (и ее подпапках). Какой была бы команда оболочки Linux для этого?
Например, я хочу запустить это для всех файлов и перезаписать старый файл новым замененным текстом.
sed 's/old text/new text/g'
Ответы:
Невозможно сделать это, используя только sed. Вам нужно будет использовать вместе хотя бы утилиту find:
find . -type f -exec sed -i.bak "s/foo/bar/g" {} \;
Эта команда создаст
.bak
файл для каждого измененного файла.Примечания:
-i
Аргументsed
команды является расширением GNU, поэтому, если вы работаете в эту команду с BSD этоsed
вам нужно будет перенаправить вывод в новый файл , то переименовать его.find
Утилита не реализует-exec
аргумент в старых коробках UNIX, поэтому вам нужно будет использовать| xargs
вместо.источник
\;
?-i
сам по себе файл резервной копии не создается, и именно это заставляет sed выполнить операцию с файлом на месте.{}
?{}
будет заменен каждым именем файла, найденнымfind
и\;
сообщает, что команда, которую он должен выполнить, завершается на этом этапе.Я предпочитаю использовать
find | xargs cmd
over,find -exec
потому что это легче запомнить.В этом примере глобально заменяется «foo» на «bar» в файлах .txt в текущем каталоге или ниже него:
find . -type f -name "*.txt" -print0 | xargs -0 sed -i "s/foo/bar/g"
Параметры
-print0
и-0
можно не указывать, если ваши имена файлов не содержат необычных символов, таких как пробелы.источник
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' "s/foo/bar/g"
(обратите внимание на то, что в-i
аргументе указывается пустая строка ).sed -i.bak
вместоsed -i
. Я думаю, что, как упоминал @JakubKukul,sed -i ''
тоже работает.Что касается переносимости, я не полагаюсь на особенности sed, специфичные для Linux или BSD. Вместо этого я использую
overwrite
сценарий из книги Кернигана и Пайка о среде программирования Unix.Затем команда
find /the/folder -type f -exec overwrite '{}' sed 's/old/new/g' {} ';'
И
overwrite
сценарий (который я использую повсюду) таков:#!/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
Идея состоит в том, что он перезаписывает файл только в случае успешного выполнения команды. Полезно,
find
а также там, где вы не хотели бы использоватьsed 's/old/new/g' file > file # THIS CODE DOES NOT WORK
потому что оболочка обрезает файл до того, как
sed
сможет его прочитать.источник
Могу я предложить (после резервного копирования ваших файлов):
find /the/folder -type f -exec sed -ibak 's/old/new/g' {} ';'
источник
Пример: замените {AutoStart} на 1 для всех ini-файлов в папке / app / config / и ее дочерних папках:
sed 's/{AutoStart}/1/g' /app/config/**/*.ini
источник
for i in $(ls);do sed -i 's/old_text/new_text/g' $i;done
источник
Это сработало для меня (на терминале Mac, в Linux вам не нужно
'' -e
):sed -i '' -e 's/old text/new text/g' `grep 'old text' -rl *`
команда
grep 'old text' -rl *
выводит список всех файлов в рабочем каталоге (и подкаталогах), где существует «старый текст». Затем это передается в sed.источник
Возможно, захочется попробовать мой сценарий массового поиска / замены Perl . Имеет некоторые преимущества по сравнению с решениями на основе цепочки (например, отсутствие необходимости иметь дело с несколькими уровнями интерпретации метасимволов оболочки).
#!/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"; }
источник
В случае, если имя файлов в папке имеет некоторые обычные имена (например, файл1, файл2 ...), я использовал для цикла.
for i in {1..10000..100}; do sed 'old\new\g' 'file'$i.xml > 'cfile'$i.xml; done
источник