dir

dir (디렉터리 목록)

디렉터리에 어떤 파일들이 들어 있는지 나열해야 할 때가 많죠. 셸에서 ls를 쓰듯이, Raku 코드에서도 디렉터리 목록을 얻을 수 있어요. dir 루틴이 바로 그 일을 도와줘요.

출처: Raku Documentation — dir

본문

IO::Path에서

multi  dir(*%_)
multi  dir(IO::Path:D $path, |c)
multi  dir(IO()       $path, |c)
method dir(IO::Path:D: Mu :$test = $*SPEC.curupdir)

디렉터리 안의 항목에 해당하는 IO::Path 객체들을 **게으른 목록(lazy list)**으로 돌려줘요. :test 파라미터를 주면 항목의 이름을 문자열로 스마트매치해서 걸러낼 수도 있어요. 목록 안의 순서는 파일시스템이 항목을 돌려주는 순서를 그대로 따르고, 특수 디렉터리 항목인 ...는 포함되지 않아요. $path를 어떻게 주느냐에 따라 결과 경로가 절대 경로가 될지 상대 경로가 될지 정해져요.

여기서 한 가지 주의할 점이 있어요. :test로 걸러내는 검사는 IO 객체가 아니라 Str 인자에 대해 이뤄져요. 그래서 검사는 대상 디렉터리가 아니라 $*CWD에서 실행돼요. 파일 테스트 연산자를 대상으로 검사할 때는 이렇게 쓰면 동작하지 않아요.

dir('mydir', test => { .IO.d })

반면 이렇게 쓰면 잘 동작해요.

dir('mydir', test => { "mydir/$_".IO.d })

참고: dir 호출은 디렉터리를 읽기 위해 여는데, 이게 프로그램의 프로세스당 최대 열린 파일 수에 한 몫을 차지해요. 그래서 재귀적으로 dir을 더 호출하기 전에, 돌려받은 Seq를 반드시 소진(exhaust)해야 해요. @ 시길 변수에 할당하거나 그냥 루프를 돌리면 소진되죠. 아래 예시에서도 추가로 살펴볼 디렉터리들을 Array에 밀어 넣고, 바로 dir을 호출하지 않는 걸 볼 수 있어요. 디렉터리 핸들 닫는 걸 더 세밀하게 제어하고 싶다면 IO::Dir 모듈도 함께 참고하면 좋아요.

예시를 볼게요.

# 현재 디렉터리의 내용을 순회하려면:
for dir() -> $file {
    say $file;
}

# 위와 같지만, 기본 :test 매처에서 걸러지는 '.'과 '..'도 포함하려면:
for dir(test => *) -> $file {
    say $file;
}

# 현재 사용자 홈 디렉터리에서 모든 .jpg, .jpeg 파일 이름을 얻으려면:
my @jpegs = $*HOME.dir: test => /:i '.' jpe?g $/;

모든 파일과 디렉터리를 재귀적으로 나열하는 예제 프로그램이에요.

sub MAIN($dir = '.') {
    my @todo = $dir.IO;
    while @todo {
        for @todo.pop.dir -> $path {
            say $path.Str;
            @todo.push: $path if $path.d;
        }
    }
}

현재 디렉터리에서 시작해 재귀적으로 .raku로 끝나는 파일 세 개를 찾는 게으른 방법이에요.

my @stack = '.'.IO;
my $raku-files = gather while @stack {
    with @stack.pop {
        when :d { @stack.append: .dir }
        .take when .extension.lc eq 'raku'
    }
}
.put for $raku-files[^3];