티스토리 뷰

2002-9-2

Globbing

디렉토리 내에 있는 파일 중 특정 확장자의 파일만 따로 모아야 할 경우가 많이 있습니다. 이처럼 파일 이름을 어떤 패턴에 맞는 것만 모으는 것을 globbing이라고 합니다. 펄에서는 glob "pattern";의 형태로 씁니다. 예를 들어 내 문서 디렉토리로 이동해서 확장자가 .txt인 파일의 이름만 따로 모으고 싶다면,

 

chdir "/home/linuxer/doc" || die "can't cd";
@text_files = glob "*.txt";
print "@text_files";

 

@text_files 내에는 확장자가 .txt인 파일의 이름이 들어갑니다. glob " " 내에는 여러 가지 패턴을 쓸 수 있습니다.  확장자가 .html 인 파일 중 b로 시작하는 파일만 모으고 싶다면,

 

@b_files = glob "b*.html";

 

또한 따옴표 내에서는 variable interpolation이 가능하므로 변수 이름을 사용할 수 있습니다.

 

$mydir = "/home/linuxer/";
@mp3 = glob "$mydir/*.mp3";

 

일반적인 큰 따옴표와 마찬가지 기능입니다.


어떤 디렉토리 내의 파일 중에 실행가능한(executable) 파일만 모으고 싶다면 파일 테스트를 활용해서 이렇게 하면 됩니다.

 

@files = glob "*";
foreach $file (@files) {
push @xcutable, $file if -x $file
}
print "@xcutable";

 

foreach 안의 push 구문이 혹시 조금 생소해 보일 수 있는데요.
그 구문은,

 

if (-x $file) {
push @xcutable, $file
}

 

와 똑같은 것입니다. 한 줄로 쓰면 훨씬 깔끔하고 이런 스타일이 펄 스타일입니다. 처음에는 if ( ) { } 형태로 차근차근 코딩해서 익히는 게 좋지만 익숙해지고 나면 위와 같이 한 줄로 줄여서 코딩하는 것도 좋습니다. 

 

같은 식으로 위의 코드는 다음과 같이 더 줄일 수도 있습니다. glob "*"을 아예 if () 안에 집어넣을 수 있습니다.

foreach $file (glob "*") {
push @xcutable, $file if -x $file
}
print "@xcutable";

"There is more than one way to do it." 펄의 모토입니다.

 

오래된 펄 코드에서는 파일 이름 글로빙을 glob대신 <>을 사용한 경우도 있습니다. <> 사이에 패턴을 넣은 것입니다. 이런 방식은 파일핸들 읽어 들이는것과 혼동할 수 있으므로 가급적 사용하지 않는 게 좋고, 다른 사람의 코드를 읽기 위해 참고만 하세요.

 

@files = </home/linuxer/*.mp3>;
@files = glob ("/home/linuxer/*.mp3");

 

두 문장은 같은 것입니다.

그리고, glob ""은 파일 이름을 a,b,c... 순서로 반환합니다. 또 하나, invisible file(점으로 시작하는 파일)의 이름은 담기지 않습니다.

 

glob "" 대신에 디렉토리 핸들을 쓸 수도 있습니다. 디렉토리 핸들은 파일핸들과 유사합니다. 단지 open()대신 opendir()를 쓰고, 디렉토리 내의 파일 이름은 readdir()를 이용해서 읽어 온다는 점이 다릅니다.

 

$dir = "/usr/local/bin";
opendir DH, $dir || die "can't open";
# 디렉토리 핸들 DH 로 "/usr/local/bin" 을 열고
@files = readdir DH; # 디렉토리 핸들을 읽어서 @files 에 담고
foreach $file (@files) {
print "$file\n"
}

 

glob ""과 다른 점은 숨김 파일(invisible file)까지 다 읽온다는 점입니다.

파일 정보 그리고 펄에서 날짜 처리

어떤 파일에 관한 모든 정보를 한꺼번에 알려주는 함수가 있습니다. 바로 stat() 함수입니다.

 

my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
$atime, $mtime, $ctime, $blksize, $blocks) = stat($filename);

 

파일 이름을 넣어 주면 그 파일의 파일시스템 상 device number, inode number, mode(파일 타입과 퍼미션), 하드링크의 갯수, user-ID, group-ID, device identifier, 파일의 크기(바이트 단위), 억세스된 시간, 변경된 시간, inode 변경 시간, 파일시스템 I/O를 위해 필요로 하는 블럭 크기, 실제로 할당된 블럭 크기 등이 리턴됩니다. 그야말로 종합적인 파일 정보를 다 얻어내는 것입니다. 

 

 

위에서 파일 변경한 시간과 억세스한 시간을 알 수 있다고 했는데, 조금 더 자세히 알아보겠습니다. 예를 들어, index.html이라는 파일의 변경한 시간, 억세스한 시간을 출력하는 코드는 이렇습니다.

 

my ($atime, $mtime) = ((stat("index.html"))[8,9];
print "$atime\n$mtime\n";

 

stat()에 의해 반환되는 여러 파일 정보 중 9번 째, 10번 째가 억세스한 시간, 변경한 시간이기 때문에 [8,9]가 된 것에 주의하세요. 배열은 0부터 셉니다. 특기할 만한 것은 stat() 앞 뒤를 또 하나의 괄호로 감쌌다는 점입니다. 그 괄호를 빼면 위 코드는 에러가 납니다. 어떤 값, 변수에 괄호를 씌우는 것은 그 값/변수를 배열로 취급하겠다는 것을 의미합니다. 즉 괄호로 감싸진 것은 리스트 컨텍스트가 되는 것입니다. 위의 경우 stat()에 의해 반환된 값들은 부가적인 괄호에 의해 리스트로 취급하게 되고, 그 결과 (list)[8,9]의 형태가 될 수 있는 것입니다. 

 

다시 주제로 돌아와서, 위 코드를 실행해보면 이런 식으로 나옵니다.

 

1031069374
1030907766

 

시간이 이런 형태로 나오면 바로 활용하기는 어렵습니다. 위 숫자는 1970년 자정을 기점으로 초 단위로 센 숫자입니다. 이런 숫자를 우리가 쉽게 사용할 수 있는 형태로 바꾸는 함수가 localtime() 함수입니다. 이를 테면 1031069374를 우리가 알 수 있는 형태로 바꾸려면,

 

$atime = 1031069374;
my ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = localtime $atime;

 

그런데 이렇게 해도 우리가 생각한 것과 약간 다릅니다.

 

첫째, $mon의 경우 을 알려주기는 하지만 0부터 11까지로 셉니다.
둘째, $year 역시 끝 두 자리만을 알려 줍니다. 1989 년이면 89가 담기는 것이고 2002 년은 102 가 담깁니다. 따라서 우리가 원하는 최종 결과를 위해서는 이렇게 해야 합니다.

 

$atime = 1031069374;
my ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = localtime $atime;
$mon++;
$year += 1900;
print "$year/$mon/$day , $hour:$min:$sec";

 

$mon은 1을 더해 주고, $year는 1900 을 더해 주면 우리가 사용하는 일반적인 월과 해가 담기게 됩니다.

$wday에는 요일이 담기는데 월요일은 1, 화요일은 2, 수요일은 3,... 의 숫자가 담깁니다.

 

만약 오늘 날짜를 년도/월/일 형태로 출력하려면 이렇게 할 수도 있습니다.

 

my ($day, $mon, $year) = (localtime)[3,4,5];
$mon++; $year+=1900;
print "$year/$mon/$day";

 

localtime에 괄호를 감싸면 현재 시간을 리스트 형태로 반환합니다.

반응형
댓글