简体   繁体   English

递归查找/替换文件,但仅触摸匹配的文件

[英]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,sedack尝试了很多在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: 附属问题:

  1. How's my perl script ? 我的perl脚本怎么样? Not too bad ? 还不错吗? Do I really need to reopen every files that match my search_pattern ? 我真的需要重新打开每个与search_pattern匹配的文件吗?

  2. 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: 不错,但有几点注意事项:

  • $do_replace is always 0 so it will not replace $ do_replace始终为0,因此不会替换
  • in-place open F, "+>" will not work on cygwin + windows 就地打开F,“ +>”在cygwin + Windows上不起作用
  • m/($search_pattern)/o /o is good, () is not needed. m /($ search_pattern)/ o / o很好,不需要()。
  • $file_pattern is ignored, you overwrite it with your own $ file_pattern被忽略,您用自己的覆盖
  • 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匹配的文件吗?

  • You don't do. 你不做

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. 以下是代码的清理版本。

  • Always include use strict; 始终包括use strict; and use warnings at the top of EVERY perl script. 并在每个perl脚本的顶部use warnings If you're doing file processing, include use autodie; 如果您正在执行文件处理,请包括use autodie; as well. 也一样
  • Go ahead and slurp the entire file. 继续并处理整个文件。 That way you only have to read and write optionally write it once. 这样,您只需要读写一次即可。
  • Consider using 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有效,在这种情况下,实际上可能是首选的模块,但是我喜欢后者的接口。
  • I removed the capture groups from the regex. 我从正则表达式中删除了捕获组。 In ones in the RHS were a bug, and the ones in the LHS were superfluous. 在RHS中,有一个是错误,而在LHS中有一个是多余的。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM