[英]Find/Replace in files recursively but touch only files with matches
I would like to quickly search and replace with or without regexp in files recursively. 我想以递归方式快速搜索和替换有或没有正则表达式的文件。 In addition, I need to search only in specific files and I do not want to touch the files that do not match my search_pattern otherwise git will think all the parsed files were modified (it what happens with find . --exec sed).
另外,我只需要搜索特定文件,并且我不想触摸与我的search_pattern不匹配的文件,否则git会认为所有解析的文件都已修改(find。--exec sed会发生这种情况)。
I tried many solutions that I found on internet using find, grep, sed or ack but I don't think they are really good to match specific files only. 我使用find,grep,sed或ack尝试了很多在Internet上找到的解决方案,但我认为它们仅匹配特定文件并不是很好。
Eventually I wrote this perl script: 最终我写了这个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, ".");
My question is: 我的问题是:
Is there any better solution like this one below (which not exists) ? 下方是否有类似这样的更好解决方案(不存在)?
`repaint -n/(.+)[.](c|h|inc|asm|mac|def|ldf|rst)$/ s/search/replacement/g .`
Subsidiary questions: 附属问题:
How's my perl script ? 我的perl脚本怎么样? Not too bad ?
还不错吗? Do I really need to reopen every files that match my search_pattern ?
我真的需要重新打开每个与search_pattern匹配的文件吗?
How people deal with this trivial task ? 人们如何处理这项琐碎的任务? Almost every good text editor have a "Search and Replace in files" feature, but not vim.
几乎每个好的文本编辑器都具有“在文件中搜索和替换”功能,但没有vim。 How vim users can do this ?
vim用户如何做到这一点?
Edit: 编辑:
I also tried this script ff.pl with ff | xargs perl -pi -e 's/foo/bar/g'
我也用
ff | xargs perl -pi -e 's/foo/bar/g'
尝试了这个脚本ff.pl。 ff | xargs perl -pi -e 's/foo/bar/g'
but it doesnt work as I expected. ff | xargs perl -pi -e 's/foo/bar/g'
但是它没有按我预期的那样工作。 It created a backup .bak even though I didn't give anything after the -pi. 它创建了一个备份.bak,即使在-pi之后我什么也没给出。 It seems it is the normal behaviour within cygwin but with this I cannot really use
perl -pi -e
看来这是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, ".");
Reedit: 重新编辑:
I finally came across this solution (under cygwin I need to remove the backup files) 我终于遇到了这个解决方案(在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
Not bad, but several minor notes: 不错,但有几点注意事项:
s/($search_pattern)/($replace_pattern)/g; s /($ search_pattern)/($ replace_pattern)/ g;
() is unneeded and will actually disturb a counter in the $replace_pattern ()是不必要的,实际上会干扰$ replace_pattern中的计数器
/(.+)[.](c|h|inc|asm|mac|def|ldf|rst)$/ should be written as /(.+)[.](c|h|inc|asm|mac|def|ldf|rst)$/应该写成
/\\.(c|h|inc|asm|mac|def|ldf|rst)$/ and maybe /i also /\\.(c|h|inc|asm|mac|def|ldf|rst)$/也可能是/ i
Do I really need to reopen every files that match my search_pattern ? 我真的需要重新打开每个与search_pattern匹配的文件吗?
Have no idea about vim, I use emacs, which has several method to accomplish this. 我不知道vim,而是使用emacs,它有几种方法可以完成此操作。
What's wrong with the following command? 以下命令有什么问题?
:grep foo **/*.{foo,bar,baz}
:cw
It won't cause any problem with any VCS and is pretty basic Vimming. 它不会对任何VCS造成任何问题,并且是非常基本的Vimming。
You are right that Vim doesn't come with a dedicated "Search and Replace in files" feature but there are plugins for that. 没错,Vim没有专门的“在文件中搜索和替换”功能,但是有一些插件 。
why not just: 为什么不只是:
grep 'pat' -rl *|xargs sed -i 's/pat/rep/g'
or I didn't understand the Q right? 还是我不明白Q对吗?
The following is a cleaned up version of your code. 以下是代码的清理版本。
use strict;
use strict;
and use warnings
at the top of EVERY perl script. use warnings
。 If you're doing file processing, include use autodie;
use autodie;
as well. File::Find::Rule
for cases like this. File::Find::Rule
。 Your implmentation using File::Find
works, and actually is probably the preferred module in this case, but I like the interface for the latter. File::Find
有效,在这种情况下,实际上可能是首选的模块,但是我喜欢后者的接口。 And the code: 和代码:
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, ".");
I suggest find2perl
if it doesn't work out of the box, you can tweak the code it generates: 我建议使用
find2perl
如果它不能立即使用,您可以调整它生成的代码:
find2perl /tmp \! -name ".*?\.(c|h|inc|asm|mac|def|ldf|rst)$" -exec "sed -e s/aaa/bbb/g {}"
it will print the following code to stdout: 它将以下代码输出到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 !$?;
}
If you want to execute, you can pipe it to perl: 如果要执行,可以将其通过管道传递给perl:
find2perl /tmp \! -name ".*?\.(c|h|inc|asm|mac|def|ldf|rst)$" -exec "sed -e s/aaa/bbb/g" | perl
You can try this plugin for Vim: https://github.com/skwp/greplace.vim 您可以尝试使用Vim的此插件: https : //github.com/skwp/greplace.vim
Basically, it allows you to type in a search phases (with/without regex) and ask you for the files to search in. 基本上,它允许您键入搜索阶段(使用/不使用正则表达式),并询问您要搜索的文件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.