요 며칠 C 코드에 있는 특정 문자열에 대한 처리를 가지고 글을 몇개 올리고 있었습니다. Java 로 먼저 짠 프로그램을 Perl 로 변환하는 작업을 하고 있는데요, 하다 보니 Perl 에 대해 좀더 많이 알아가는 것 같습니다.
오늘 올릴 글은 한국 펄 사용자 모임에 올라온 Tip 중의 하나 입니다. Java 로 작성한 프로그램에서 입력값으로 디렉토리도 받을 수 있도록 하고 있었습니다. 입력된 값이 디렉토리인 경우, 하위 디렉토리에 있는 C 파일에 대해서도 처리를 하도록 했습니다. 그렇게 하기 위해 traverse()라는 함수를 만들었었는데요, 이를 Perl로 구현하기 위해 관련 자료를 찾던 중 알게되었습니다.
방법은 간단합니다. File::Find 를 쓰면 무척 쉽더군요.
find( \&traverse, 'target'); # File:Find 의 Search Function
sub traverse {
my $filename = $File::Find::name;
if( (-f $_) && ($filename =~ /\.c$/))
{ # c 파일만 처리
replace( $filename);
}
} # end of sub traverse
sub traverse {
my $filename = $File::Find::name;
if( (-f $_) && ($filename =~ /\.c$/))
{ # c 파일만 처리
replace( $filename);
}
} # end of sub traverse
find 의 인자로 subroutine 과 초기 디렉토리를 넘겨줍니다. 그러면 find는 하위 디렉토리를 검색하면서 각 항목에 대해 find 호출 시 받은 subroutine을 호출하게 됩니다. 위의 프로그램에서는 traverse 가 불리게 됩니다. $File::Find::name 은 파일 이름을, $File::Find::dir은 해당 파일에 대한 디렉토리 명을 알려줍니다. 만약 target이라는 하위 디렉토리에 A.c B.c C.c 라는 파일이 있다면 $File::Find::name 의 값은 각각 target/A.c target/B.c tartget/C.c 가 되고 $File::Find::dir 의 값은 target 이 됩니다.
!! 주의
File::Find
CPAN에 있는 설명을 보면 아래와 같은 문구가 있습니다.
find() does a depth-first search over the given @directories in the order they are given. For each file or directory found, it calls the &wanted subroutine. (See below for details on how to use the &wanted function). Additionally, for each directory found, it will chdir() into that directory and continue the search, invoking the &wanted function on each file or subdirectory in the directory.find() 에 의해 자동으로 디렉토리가 바뀌는 점에 주의를 기울여야 합니다. 위의 코드에서는 replace($filename)으로 호출하고 있지만 엄밀히 따지면 전체 패스를 줄 필요가 없습니다. 이미 디렉토리가 변경되어 있기 때문이죠. 이는 매우 편리할 수도, 그리고 매우 위험할 수도 있습니다. 프로그램을 작성할 때 위의 사항에 유의해야만 합니다.
[추가]
Aero 님께서 좋은 정보를 주셔서 찾아보니 CPAN의 설명에 no_chdir 이라는 옵션이 있더군요. 그래서 다음과 같이 바꾸어 보았습니다.
find( {wanted => \&traverse, no_chdir => 1 }, 'target');
sub traverse {
my $filename = $_;
if( (-f $filename) && ($File::Find::name =~ /\.c$/))
{
replace( $filename);
}
sub traverse {
my $filename = $_;
if( (-f $filename) && ($File::Find::name =~ /\.c$/))
{
replace( $filename);
}
그리고, 더불어 펄 사용자 모임에 있는 Tip 하나를 더 소개합니다. 경로에서 디렉토리와 파일 이름 분리라는 글인데요, Regular Expression을 이용한 멋진 방법이네요.
( $dir, $file ) = ( $srcfilename =~ m|^(.*/)(.*)$| );
Regular Expression의 Greedy 특성을 이용한 방법으로 정말 멋지게 동작합니다 ^^
댓글을 달아 주세요
File::Find의 find함수에서 디렉토리를 안 바뀌게 하는 옵션도 있습니다.
2008/12/17 14:04http://kldp.org/node/101006#comment-469299 를 참고하시면 될 듯.
CPAN을 찾아보니 no_chdir 이라는 옵션이 있더군요. 좋은 정보 감사합니다.
2008/12/17 15:12안녕하세요. 유용한 글 잘 읽었습니다. :-) 정규표현식을 이용한 간단한 디렉터리 및 파일 분리도 좋지만 또 한편으로는 기본 모듈로 포함하고 있는(설치가 필요없는) File::Basename 가 제공하는 fileparse(), basename(), dirname()을 사용하면 운영체제 별로 신경쓰지 않아도 된다는 면에서 괜찮은 것 같습니다. 새해 복 많이 받으세요~ :-)
2009/01/03 18:33