폴더 (및 하위 폴더)의 모든 파일에서 정규식 찾기 및 바꾸기를 수행해야합니다. 이를 수행하는 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
대신 a를 사용해야합니다 .\;
?
-i
자체로는 백업 파일을 생성하지 않으며 sed가 파일에 대한 작업을 제자리에서 수행하게하는 원인이됩니다.
{}
?
{}
발견 각각의 파일 이름으로 대체됩니다 find
하고 \;
그 명령 그는이 시점에서 마감을 실행하는 데 필요한 찾을 알려줍니다.
기억하기 쉽기 때문에 find | xargs cmd
over 사용하는 것을 선호합니다 find -exec
.
이 예제는 현재 디렉토리 또는 그 아래의 .txt 파일에서 "foo"를 "bar"로 전역 적으로 대체합니다.
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 ''
작동 한다고 생각 합니다.
이식성을 위해 linux 또는 BSD에 특정한 sed 기능에 의존하지 않습니다. 대신 overwrite
Kernighan과 Unix Programming Environment에 대한 Pike의 책에서 스크립트를 사용합니다 .
명령은 다음과 같습니다.
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
을 읽기 전에 잘라 내기 때문 입니다.
for i in $(ls);do sed -i 's/old_text/new_text/g' $i;done
내 대량 검색 / 대체 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";
}
폴더의 파일 이름에 정규 이름 (예 : file1, file2 ...)이있는 경우주기에 사용했습니다.
for i in {1..10000..100}; do sed 'old\new\g' 'file'$i.xml > 'cfile'$i.xml; done