Regex Perl:在多个文本文件中查找并替换特定字符串
我需要获取给定目录中的所有.config文件,在每个文件中,我需要搜索一个特定字符串,并根据该文件替换为另一个字符串 例如,如果给定目录中有3个文件:Regex Perl:在多个文本文件中查找并替换特定字符串,regex,perl,shell,replace,Regex,Perl,Shell,Replace,我需要获取给定目录中的所有.config文件,在每个文件中,我需要搜索一个特定字符串,并根据该文件替换为另一个字符串 例如,如果给定目录中有3个文件: for my_foo.config - string to search "fooCommon >" replace with "~ /fooCommon[\/ >" for my_bar.config - string to search "barCommon >" replace with "~ /barCommon
for my_foo.config - string to search "fooCommon >" replace with "~ /fooCommon[\/ >"
for my_bar.config - string to search "barCommon >" replace with "~ /barCommon[\/ >"
for my_file.config - string to search "someCommon >" replace with "~ /someCommon[\/ >"
请让我知道如何在Perl中实现这一点
下面是我在shell脚本中尝试的代码:
OLD="\/fooCommon >"
NEW="~ \"\/fooCommon[^\/]*\" >"
DPATH="/myhome/aru/conf/host*.conf"
BPATH="/myhome/aru/conf/bakup"
TFILE="/myhome/aru/out.tmp.$$"
[ ! -d $BPATH ] && mkdir -p $BPATH || :
for f in $DPATH
do
if [ -f $f -a -r $f ]; then
/bin/cp -f $f $BPATH
echo sed \"s\/$OLD\/$NEW\/g\"
sed "s/$OLD/$NEW/g" "$f" > $TFILE && mv $TFILE "$f"
else
echo "Error: Cannot read $f"
fi
done
/bin/rm $TFILE
Perl在这里只是为了修改文件。。。我不明白为什么要用perl完整地编写它,如果您可以像这样做得更简单:
find . -maxdepth 1 -type f -name '*.conf' | \
xargs perl -i.bak -pe 's/localhost/example.com/;'
如果您是在类Unix平台上,那么可以在命令行上使用Perl来完成;不需要写脚本
perl -i -p -e 's/old/new/g;' *.config
为了安全起见,您可能希望将命令与备份选项一起使用
perl -i.bak -p -e 's/old/new/g;' *.config
如果您真的只需要使用perl来实现这一点,我不推荐使用perl,因为已经发布了一些非常好、更简单的答案,下面是:
#!/usr/bin/perl
# take the directory to be processed from first command line argument
opendir($dh, $ARGV[0]);
# take only relevant files ie. "*.config"
@cfgs = grep { /\.config$/ } readdir($dh);
# loop through files
foreach(@cfgs) {
# generate source string from the filename
($s) = ($_ =~ /.*_(\w+)\.config.*/);
$s = "${s}Common";
# generate replacement string from the filename
$r = "~ /${s}[/ >";
# move original file to a backup
rename("${ARGV[0]}${_}", "${ARGV[0]}${_}.bak");
# open backup file for reading
open(I, "< ${ARGV[0]}${_}.bak");
# open a new file, with original name for writing
open(O, "> ${ARGV[0]}${_}");
# go through the file, replacing strings
while(<I>) { $_ =~ s/$s/$r/g; print O $_; }
# close files
close(I);
close(O);
}
# end of file.
#/usr/bin/perl
#从第一个命令行参数获取要处理的目录
opendir($dh,$ARGV[0]);
#仅获取相关文件,如“*.config”
@cfgs=grep{/\.config$/}readdir($dh);
#循环浏览文件
foreach(@cfgs){
#从文件名生成源字符串
($s)=($\u=~/.*w(\w+)\.config.*/);
$s=“${s}公共”;
#从文件名生成替换字符串
$r=“~/${s}[/>”;
#将原始文件移动到备份
重命名(${ARGV[0]}${{},“${ARGV[0]}${{}.bak”);
#打开备份文件进行读取
打开(I,“<${ARGV[0]}${{u}.bak”);
#打开一个新文件,使用原始名称进行写入
打开(O,“>${ARGV[0]}${{}”);
#检查文件,替换字符串
while()
#关闭文件
关闭(I);
关闭(O);
}
#文件结束。
请注意,使用简单的find和/或shell通配符执行此操作要简单得多。但请将此作为如何使用perl处理文件的小教程。以下内容可能会有所帮助:
use strict;
use warnings;
my %replacements =
map { chomp; my @x = split /\|/; $x[0] => [ $x[1], $x[2] ] } <DATA>;
local $^I = '.bak';
for my $file (<*.config>) {
push @ARGV, $file;
while (<>) {
s/\b\Q$replacements{$file}[0]/$replacements{$file}[1]/g;
print;
}
}
__DATA__
my_foo.config|fooCommon >|~ /fooCommon[/ >
my_bar.config|barCommon >|~ /barCommon[/ >
my_file.config|someCommon >|~ /someCommon[/ >
使用严格;
使用警告;
我的替换百分比=
映射{chomp;my@x=split/\|/;$x[0]=>[$x[1],$x[2]};
本地$^I='.bak';
对于我的$file(){
推送@ARGV$file;
而(){
s/\b\Q$replacements{$file}[0]/$replacements{$file}[1]/g;
印刷品;
}
}
__资料__
my_foo.config|foocomon>| ~/foocomon[/>
my_bar.config|barCommon>| ~/barCommon[/>
my_file.config | someCommon>| ~/someCommon[/>
数组散列(HoA)是通过split
划分|
分隔的数据行构建的,其中键是文件名,值是对匿名数组的引用,匿名数组的两个元素用于文件上的替换。local$^I='.bak'
符号创建原始文件的备份
您可能需要调整替换。例如,通过使用s/\b\Q$replacements{$file}[0]/$replacements{$file}[1]/g;
中的\b
在替换中观察单词边界。您可能需要也可能不需要(或不想要)此选项
我建议您先在一个“scratch”文件上试用它,以确保在完全实现它之前获得您想要的结果——即使原始文件已备份。虽然可以从命令行完成,但有时您只需要一个易于使用的脚本,提供更有用的输出perl解决方案,具有友好的输出,适用于遇到此问题的任何人
#!/usr/bin/env perl5.8.3
# subst [-v] [-f] "re/string to find" "string to replace" -- list of files
# optional -v flag shows each line with replacement, must be 1st arg to script
# optional -f flag says to disable regexp functionality and make the strings match exactly
# replacement string may include back references ($1, $2, etc) to items in "string to find" if they are surrounded by grouping parenthesis
use strict;
use warnings;
use List::Util;
use IO::File;
use Fcntl;
use Getopt::Long qw(GetOptions);
my $verbose = 0;
my $fixed = 0;
GetOptions("v" => \$verbose,
"f" => \$fixed);
my $find = shift @ARGV;
my $replace = shift @ARGV;
die "Error: missing 1st arg, string to find\n" if not defined $find;
die "Error: missing 2nd arg, string to replace with\n" if not defined $replace;
die "No files were specified\n" if @ARGV == 0;
# open a temp file for writing changes to
my $TEMP = IO::File->new_tmpfile;
if (not defined $TEMP)
{
print STDERR "ERROR: failed to create temp file: $!\n";
exit 1;
}
# Fix max file name width for printing
my $fwidth = List::Util::max map { length $_ } @ARGV;
# Process each file
my $unchanged = 0;
my $changed = 0;
foreach my $file (@ARGV)
{
if (open(my $FILE, '<', $file))
{
# Reset temp file
seek $TEMP, 0, SEEK_SET or die "ERROR: seek in temp file failed: $!";
truncate $TEMP, 0 or die "ERROR: truncate of temp file failed: $!";
# go through the file, replacing strings
my $changes = 0;
while(defined(my $line = <$FILE>))
{
if ($line =~ m/$find/g)
{
print "-" . $line if $verbose;
print "\n" if $verbose and $line !~ m/\n$/;
if ($fixed)
{
my $index = index($line, $find);
substr($line, $index, length($find)) = $replace;
}
else
{
$line =~ s/$find/replacebackrefs($replace)/eg;
}
$changes++;
print "+" . $line if $verbose;
print "\n" if $verbose and $line !~ m/\n$/;
}
print $TEMP $line;
}
close $FILE;
if ($changes == 0)
{
$unchanged++;
unlink("/tmp/subst$$");
next;
}
# Move new contents into old file
$changed++;
printf "%*s - %3d changes\n", -$fwidth, $file, $changes;
seek $TEMP, 0, SEEK_SET or die "ERROR: rewind of temp file failed: $!";
open $FILE, '>', $file or die "ERROR: failed to re-write $file: $!\n";
while (<$TEMP>) { print $FILE $_ }
close $FILE;
print "\n" if $verbose;
}
else
{
print STDERR "Error opening $file: $!\n";
}
}
close $TEMP;
print "\n";
print "$changed files changed, $unchanged files unchanged\n";
exit 0;
sub replacebackrefs
{
# 1st/only argument is the text matched
my $matchedtext = shift @_;
my @backref;
# @- is a dynamic variable that holds the offsets of submatches in
# the currently active dynamic scope (i.e. within each regexp
# match), corresponding to grouping parentheses. We use the count
# of entrees in @- to determine how many matches there were and
# store them into an array. Note that @- index [0] is not
# interesting to us because it has a special meaning (see man
# perlvar for @-)\, and that backrefs start with $1 not $0.
# We cannot do the actual replacement within this loop.
do
{
no strict 'refs'; # turn of warnings of dynamic variables
foreach my $matchnum (1 .. $#-)
{
$backref[$matchnum] = ${$matchnum}; # i.e. $1 or $2 ...
}
} while(0);
# now actually replace each back reference in the matched text
# with the saved submatches.
$matchedtext =~ s/\$(\d+)/$backref[$1]/g;
# return a scalar string to actually use as the replacement text,
# with all the backreferences in the matched text replaced with
# their submatch text.
return $matchedtext;
}
#!/usr/bin/env perl5.8.3
#subst[-v][-f]“re/string以查找”“要替换的字符串”--文件列表
#可选-v标志显示替换的每一行,必须是脚本的第一个参数
#可选-f标志表示禁用regexp功能并使字符串完全匹配
#替换字符串可能包括对“要查找的字符串”中的项的反向引用($1、$2等),如果它们被分组括号包围
严格使用;
使用警告;
使用列表::Util;
使用IO::文件;
使用Fcntl;
使用Getopt::longqw(GetOptions);
我的$verbose=0;
我的$fixed=0;
GetOptions(“v”=>\$verbose,
“f”=>\$fixed);
我的$find=shift@ARGV;
my$replace=shift@ARGV;
die“错误:缺少第一个参数,要查找的字符串\n”,如果未定义$find;
die“错误:缺少第二个参数,要替换的字符串\n”如果未定义$replace;
如果@ARGV==0,则为“未指定文件\n”;
#打开临时文件以将更改写入
my$TEMP=IO::File->new\u tmpfile;
如果(未定义$TEMP)
{
打印标准“错误:无法创建临时文件:$!\n”;
出口1;
}
#修复用于打印的最大文件名宽度
my$fwidth=List::Util::max map{length$\uz}@ARGV;
#处理每个文件
我的$unchange=0;
我的$changed=0;
foreach my$文件(@ARGV)
{
如果(打开(我的$文件,,$文件或死亡)错误:无法重新写入$文件:$!\n;
while(){print$FILE$}
关闭$FILE;
如果$verbose,则打印“\n”;
}
其他的
{
打印STDERR“打开$文件时出错:$!\n”;
}
}
关闭$TEMP;
打印“\n”;
打印“$changed files changed,$unchanged files unchanged\n”;
出口0;
子替换背景参考
{
#第一个/唯一的参数是匹配的文本
我的$matchedtext=shift@;
我的@backref;
#@-是一个动态变量,用于保存子匹配的偏移量
#当前活动的动态范围(即在每个regexp中
#匹配),对应于分组括号。我们使用计数
#主菜的数量-以确定有多少场比赛和
#将它们存储到数组中。请注意,@-index[0]不是
#对我们来说很有趣,因为它有一个特殊的意义(见man
#“@-\”的perlvar,而backref以$1开始,而不是$0。
#我们不能在这个循环中进行实际的替换。
做
{
没有严格的“参考”;#动态变量警告
foreach my$matchnum(1..$#-)
{
$backref[$matchnum]=${$matchnum};#即$1或$2。。。
}
}而(0);
#现在实际替换匹配文本中的每个反向引用
#与保存的子匹配。
$matchedtext=~s/\$(\d+)/$backref[$1]/g;
#返回一个标量字符串以实际用作
bf=`basename "$f"`
grep -r OLD_STRING * | cut -d':' -f1 | uniq | xargs perl -i -pe 's/OLD_STRING/NEW_STRING/g;'