我想在递归中快速搜索和替换文件中的 regexp 。另外,我只需要搜索特定的文件,我不想触摸与 search_pattern 不匹配的文件,否则git会认为所有已解析的文件都被修改了(这与find有关。 --exec sed)。
我尝试使用 find,grep,sed 或 ack 在互联网上找到的许多解决方案,但我不认为他们真的很适合匹配特定文件只要。
最后我写了这个perl脚本:
#!/bin/perl
use strict;
use warnings;
use File::Find;
my $search_pattern = $ARGV[0];
my $replace_pattern = $ARGV[1];
my $file_pattern = $ARGV[2];
my $do_replace = 0;
sub process {
return unless -f;
return unless /(.+)[.](c|h|inc|asm|mac|def|ldf|rst)$/;
open F, $_ or print "couldn't open $_\n" && return;
my $file = $_;
my $i = 0;
while (<F>) {
if (m/($search_pattern)/o) {$i++};
}
close F;
if ($do_replace and $i)
{
printf "found $i occurence(s) of $search_pattern in $file\n";
open F, "+>".$file or print "couldn't open $file\n" && return;
while (<F>)
{
s/($search_pattern)/($replace_pattern)/g;
print F;
}
close F;
}
}
find(\&process, ".");
我的问题是:
下面是否有更好的解决方案(不存在)?
`repaint -n/(.+)[.](c|h|inc|asm|mac|def|ldf|rst)$/ s/search/replacement/g .`
附属问题:
我的perl脚本怎么样?还不错?我是否真的需要重新打开与search_pattern匹配的每个文件?
人们如何应对这项琐碎的任务?几乎每个优秀的文本编辑器都有&#34;搜索和替换文件&#34;功能,但不是vim。 vim用户如何做到这一点?
编辑:
我还尝试使用ff | xargs perl -pi -e 's/foo/bar/g'
这个脚本ff.pl,但它并没有像我预期的那样工作。它创建了一个备份.bak,即使我在-pi之后没有给出任何东西。这似乎是cygwin中的正常行为,但有了这个,我无法真正使用perl -pi -e
#!/bin/perl
use strict;
use warnings;
use File::Find;
use File::Basename;
my $ext = $ARGV[0];
sub process {
return unless -f;
return unless /\.(c|h|inc|asm|mac|def|ldf|rst)$/;
print $File::Find::name."\n" ;
}
find(\&process, ".");
重新编辑:
我终于遇到了这个解决方案(在cygwin下我需要删除备份文件)
find . | egrep '\.(c|h|asm|inc)$' | xargs perl -pi.winsucks -e 's/<search>/<replace>/g'
find . | egrep '\.(c|h|asm|inc)\.winsucks$' | xargs rm
答案 0 :(得分:0)
不错,但有几个小调:
S /($ search_pattern)/($ replace_pattern)/克;
()是不必要的,实际上会打扰$ replace_pattern
/(。+)[。](c | h | inc | asm | mac | def | ldf | rst)$ /应写成
/ \。(c | h | inc | asm | mac | def | ldf | rst)$ / and may / i also
我是否真的需要重新打开与search_pattern匹配的每个文件?
不知道vim,我使用emacs,它有几种方法可以实现这一点。
答案 1 :(得分:0)
以下命令有什么问题?
:grep foo **/*.{foo,bar,baz}
:cw
它不会导致任何VCS出现任何问题,并且是非常基本的Vimming。
你是对的,Vim并没有专门的&#34;搜索和替换文件&#34;功能,但有plugins。
答案 2 :(得分:0)
为什么不呢:
grep 'pat' -rl *|xargs sed -i 's/pat/rep/g'
或者我不明白Q对吗?
答案 3 :(得分:0)
以下是您的代码的清理版本。
use strict;
和use warnings
。如果您正在进行文件处理,请同时包含use autodie;
。File::Find::Rule
来处理此类情况。您使用File::Find
的实施工作,实际上可能是这种情况下的首选模块,但我喜欢后者的界面。代码:
use strict;
use warnings;
use autodie;
use File::Find;
my $search_pattern = $ARGV[0];
my $replace_pattern = $ARGV[1];
my $file_pattern = $ARGV[2];
my $do_replace = 0;
sub process {
return if !-f;
return if !/[.](?:c|h|inc|asm|mac|def|ldf|rst)$/;
my $data = do {
open my $fh, '<', $_;
local $/;
<$fh>;
};
my $count = $data =~ s/$search_pattern/$replace_pattern/g
or return;
print "found $count occurence(s) of $search_pattern in $_\n";
return if !$do_replace;
open my $fh, '>', $_;
print $fh $data;
close $fh;
}
find(\&process, ".");
答案 4 :(得分:0)
我建议find2perl
如果它没有开箱即用,你可以调整它生成的代码:
find2perl /tmp \! -name ".*?\.(c|h|inc|asm|mac|def|ldf|rst)$" -exec "sed -e s/aaa/bbb/g {}"
它会将以下代码打印到stdout:
#! /usr/bin/perl -w
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
if 0; #$running_under_some_shell
use strict;
use File::Find ();
# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.
# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name = *File::Find::name;
*dir = *File::Find::dir;
*prune = *File::Find::prune;
sub wanted;
sub doexec ($@);
use Cwd ();
my $cwd = Cwd::cwd();
# Traverse desired filesystems
File::Find::find({wanted => \&wanted}, '/tmp');
exit;
sub wanted {
my ($dev,$ino,$mode,$nlink,$uid,$gid);
(($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
! /^\..*.?\\.\(c|h|inc|asm|mac|def|ldf|rst\)\$\z/s &&
doexec(0, 'sed -e s/aaa/bbb/g {}');
}
sub doexec ($@) {
my $ok = shift;
my @command = @_; # copy so we don't try to s/// aliases to constants
for my $word (@command)
{ $word =~ s#{}#$name#g }
if ($ok) {
my $old = select(STDOUT);
$| = 1;
print "@command";
select($old);
return 0 unless <STDIN> =~ /^y/;
}
chdir $cwd; #sigh
system @command;
chdir $File::Find::dir;
return !$?;
}
如果要执行,可以将其传递给perl:
find2perl /tmp \! -name ".*?\.(c|h|inc|asm|mac|def|ldf|rst)$" -exec "sed -e s/aaa/bbb/g" | perl
答案 5 :(得分:0)
你可以为Vim试试这个插件: https://github.com/skwp/greplace.vim
基本上,它允许您输入搜索阶段(带/不带正则表达式)并询问您要搜索的文件。