2008-10-29 3 views
11

저는 프로젝트의 모든 소스 파일에서 일부 텍스트를 대체하기 위해 Perl 스크립트를 코딩하려고했습니다. 내가 좋아하는 뭔가를 필요로 해요 :대량 파일 텍스트 대체를 간단하게 수행 할 수 있습니까?

perl -p -i.bak -e "s/thisgoesout/thisgoesin/gi" *.{cs,aspx,ascx} 

하지만 그 디렉토리 재귀모든 파일을 구문 분석합니다.

난 그냥 스크립트 시작 :

use File::Find::Rule; 
use strict; 

my @files = (File::Find::Rule->file()->name('*.cs','*.aspx','*.ascx')->in('.')); 

foreach my $f (@files){ 
    if ($f =~ s/thisgoesout/thisgoesin/gi) { 
      # In-place file editing, or something like that 
    } 
} 

을하지만 지금은 갇혔어요. Perl을 사용하여 모든 파일을 편집 할 수있는 간단한 방법이 있습니까?

수정 된 모든 파일의 사본을 보관할 필요가 없습니다. 난이 그들을 모두 subversioned =)

업데이트 : 나는, Cygwin

perl -p -i.bak -e "s/thisgoesout/thisgoesin/gi" {*,*/*,*/*/*}.{cs,aspx,ascx 

을이 시도하지만 내 인수 목록이 허용되는 최대 크기로 폭발 것 같습니다.

find . -name '*.{cs,aspx,ascx}' | xargs perl -p -i.bak -e "s/thisgoesout/thisgoesin/gi" 

이 모든 파일 이름을 나열합니다 재귀, 다음 xargs 표준 입력을 읽고 나머지를 실행합니다 : 사실, 나는

+0

Windows가 실행 중임을 유의해야합니다. –

답변

13

(다이아몬드 <> 일명)/-i$^I 대신 명령 행에 지정된 것에 해당 파일에 작동합니다.

use File::Find::Rule; 
use strict; 

@ARGV = (File::Find::Rule->file()->name('*.cs', '*.aspx', '*.ascx')->in('.')); 
$^I = '.bak'; # or set `-i` in the #! line or on the command-line 

while (<>) { 
    s/thisgoesout/thisgoesin/gi; 
    print; 
} 

원하는대로 정확하게 수행해야합니다.

패턴이 여러 줄에 걸쳐있을 수 있다면 <> 앞에 undef $/;을 추가하여 Perl이 줄 단위가 아닌 한 번에 전체 파일에서 작동하도록하십시오.

+0

정확히 내가 필요한 것! – Seiti

2

당신은 find를 사용할 수 있습니다 ... Cygwin에서에 아주 이상한 오류를 받고 있어요 끝에 파일 이름이 추가 된 명령 행. xargs에 대한 좋은 점 중 하나는 빌드 된 명령 줄이 너무 길어 한 번에 실행될 수없는 경우 명령 줄을 두 번 이상 실행한다는 것입니다. 내가 find 완전히 파일을 선택 모든 쉘 방법을 이해하고 있는지 확실하지, 그래서 위의 경우 다음 작동하지 않습니다

참고 아마하려고이 같은 파이프 라인을 사용하는 경우

find . | grep -E '(cs|aspx|ascx)$' | xargs ... 

, 내가 좋아하는 명령 행을 빌드하고 진행하기 전에 각 부분을 개별적으로 실행하여 각 프로그램이 원하는 입력을 받는지 확인하십시오. 따라서 먼저 xargs없이 파트를 실행하여 확인할 수 있습니다.

당신이 그렇게 말하지는 않았지만, 찾고자하는 파일 접미사 때문에 Windows에있을 가능성이 높습니다. 이 경우 위의 파이프 라인은 Cygwin을 사용하여 실행할 수 있습니다. 당신이 시작한 것처럼 똑같은 일을하는 Perl 스크립트를 작성할 수도 있지만, 그 상황에서 -i 스위치를 사용할 수 없으므로 직접 편집해야합니다.

+0

시도한 찾기. -name '*. {cs, aspx, ascx}' 행운이지만 grep 버전에서는 파일을 나열했습니다. 좋은! 그러나 모든 명령을 실행할 때 xargs : perl : 인수 목록이 너무 깁니다. – Seiti

+0

xargs는 명령 줄의 최대 길이를 확인할 수없는 경우 각 명령 줄에 전달되는 인수의 개수를 제한 할 수도 있습니다. . 버전에 따라 xargs에 -L 또는 -n 옵션을 사용하십시오 (매뉴얼 페이지 참조). –

+0

find & xargs를 사용하려면 -print0과 -0을 사용하여 파일 이름에 공백이있는 문제를 피하십시오. find -print0 ... | xargs -0 ... – Schwern

4

변경

foreach my $f (@files){ 
    if ($f =~ s/thisgoesout/thisgoesin/gi) { 
      #inplace file editing, or something like that 
    } 
} 

foreach my $f (@files){ 
    open my $in, '<', $f; 
    open my $out, '>', "$f.out"; 
    while (my $line = <$in>){ 
     chomp $line; 
     $line =~ s/thisgoesout/thisgoesin/gi 
     print $out "$line\n"; 
    } 
} 

에이 패턴이 여러 줄에 걸쳐하지 않는 것으로 가정합니다. 패턴이 선을 넘을 수있는 경우 파일 내용을 슬럿해야합니다. ("slurp"는 꽤 일반적인 Perl 용어입니다).

씹는는 (당신이 chomp을 떨어 뜨리면, print $out $line;print $out "$line\n"; 변경) 나는 그냥 chomp는 너무 많은 시간 에드했다 라인에 물린 봤는데, 실제로 필요하지 않습니다.

마찬가지로 open my $out, '>', "$f.out";open my $out, '>', undef;으로 변경하여 임시 파일을 연 다음 대체가 완료되면 해당 파일을 원래 파일로 다시 복사 할 수 있습니다. 사실, 특히 전체 파일을 쓰다듬어 버리면 메모리에서 대체하고 원본 파일을 덮어 쓸 수 있습니다. 그러나 필자는 항상 실수로 파일을 작성하고 내용을 검증합니다.


참고, 나는 원래 그 코드에서 if 문을했다. 그럴 가능성이 가장 높습니다. 정규 표현식 "thisgoesout"(물론 "thisgoesin"으로 바꿈)과 일치하는 행을 복사했을뿐입니다. 나머지는 조용히 깜박입니다. 그

# In this example, we wish to replace 
    # the word 'foo' with the word 'bar' in several files, 
    # with no risk of ending up with the replacement done 
    # in some files but not in others. 

    use File::Transaction::Atomic; 

    my $ft = File::Transaction::Atomic->new; 

    eval { 
     foreach my $file (@list_of_file_names) { 
      $ft->linewise_rewrite($file, sub { 
       s#\bfoo\b#bar#g; 
      }); 
     } 
    }; 

    if ([email protected]) { 
     $ft->revert; 
     die "update aborted: [email protected]"; 
    } 
    else { 
     $ft->commit; 
    } 

커플을 파일로 :

7

당신은 File::Transaction::Atomic 또는 File::Transaction

F의 사용법에 관심이있을 수는 :: T는 : A는 당신이 뭘하려는 건지 매우 유사합니다 :: 당신이 이미 쓴 것을 발견하면, 당신은 잘 가야합니다.

6

Tie :: File을 사용하여 대용량 파일에 확장 가능하게 액세스하고 제자리에서 변경할 수 있습니다. 맨 페이지 (man 3perl Tie :: File)를 참조하십시오. 당신이 *ARGV를 사용하기 전에 @ARGV를 할당하면

+0

왜 Perldoc 대신에 그것들을 남자 (3perl)에게 가르쳐야합니까? – ephemient

+0

예, Tie :: File은 이런 종류의 일을 위해 만들어졌습니다. – Schwern

+0

http://perldoc.perl.org/Tie/File.html –

1

덕분에이 질문에 ephemient하고 this answer에, 나는이있어 :

use File::Find::Rule; 
use strict; 

sub ReplaceText { 
    my $regex = shift; 
    my $replace = shift; 

    @ARGV = (File::Find::Rule->file()->name('*.cs','*.aspx','*.ascx')->in('.')); 
    $^I = '.bak'; 
    while (<>) { 
     s/$regex/$replace->()/gie; 
     print; 
    } 
} 

ReplaceText qr/some(crazy)regexp/, sub { "some $1 text" }; 

지금은 해시를 통해 심지어 루프를 포함 할 수 있습니다 정규 표현식 => 서브 항목을!

+0

이 변수는 전역 효과가 있기 때문에'@ ARGV'와'$^I'를이 루틴 내에서'localize '해야합니다. – ephemient

관련 문제