フォルダー(およびそのサブフォルダー)内のすべてのファイルを正規表現で検索して置換する必要があります。それを行うための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
よりも好き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
引数に空の文字列を指定することに注意してください)。
移植性については、LinuxまたはBSDに固有のsedの機能には依存しません。代わりにoverwrite
、Unixプログラミング環境に関するKernighanと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