簡體   English   中英

遞歸查找/替換文件,但僅觸摸匹配的文件

[英]Find/Replace in files recursively but touch only files with matches

我想以遞歸方式快速搜索和替換有或沒有正則表達式的文件。 另外,我只需要搜索特定文件,並且我不想觸摸與我的search_pattern不匹配的文件,否則git會認為所有解析的文件都已修改(find。--exec sed會發生這種情況)。

我使用find,grep,sedack嘗試了很多在Internet上找到的解決方案,但我認為它們僅匹配特定文件並不是很好。

最終我寫了這個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 .`

附屬問題:

  1. 我的perl腳本怎么樣? 還不錯嗎? 我真的需要重新打開每個與search_pattern匹配的文件嗎?

  2. 人們如何處理這項瑣碎的任務? 幾乎每個好的文本編輯器都具有“在文件中搜索和替換”功能,但沒有vim。 vim用戶如何做到這一點?

編輯:

我也用ff | xargs perl -pi -e 's/foo/bar/g'嘗試了這個腳本ff.pl。 ff | xargs perl -pi -e 's/foo/bar/g'但是它沒有按我預期的那樣工作。 它創建了一個備份.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   

不錯,但有幾點注意事項:

  • $ do_replace始終為0,因此不會替換
  • 就地打開F,“ +>”在cygwin + Windows上不起作用
  • m /($ search_pattern)/ o / o很好,不需要()。
  • $ file_pattern被忽略,您用自己的覆蓋
  • s /($ search_pattern)/($ replace_pattern)/ g;

    ()是不必要的,實際上會干擾$ replace_pattern中的計數器

  • /(.+)[.](c|h|inc|asm|mac|def|ldf|rst)$/應該寫成

    /\\.(c|h|inc|asm|mac|def|ldf|rst)$/也可能是/ i

我真的需要重新打開每個與search_pattern匹配的文件嗎?

  • 你不做

我不知道vim,而是使用emacs,它有幾種方法可以完成此操作。

以下命令有什么問題?

:grep foo **/*.{foo,bar,baz}
:cw

它不會對任何VCS造成任何問題,並且是非常基本的Vimming。

沒錯,Vim沒有專門的“在文件中搜索和替換”功能,但是有一些插件

為什么不只是:

grep 'pat' -rl *|xargs sed -i 's/pat/rep/g'

還是我不明白Q對嗎?

以下是代碼的清理版本。

  • 始終包括use strict; 並在每個perl腳本的頂部use warnings 如果您正在執行文件處理,請包括use autodie; 也一樣
  • 繼續並處理整個文件。 這樣,您只需要讀寫一次即可。
  • 考慮在這種情況下使用File::Find::Rule 使用File::Find有效,在這種情況下,實際上可能是首選的模塊,但是我喜歡后者的接口。
  • 我從正則表達式中刪除了捕獲組。 在RHS中,有一個是錯誤,而在LHS中有一個是多余的。

和代碼:

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, ".");   

我建議使用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

您可以嘗試使用Vim的此插件: https : //github.com/skwp/greplace.vim

基本上,它允許您鍵入搜索階段(使用/不使用正則表達式),並詢問您要搜索的文件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM