Perl에서 리프 디렉토리 감지

Perl에서 리프 디렉토리 감지

제가 개발 중인 Perl 스크립트에 대해 다음을 찾고 있습니다.빠르게그리고믿을 수 있는지정된 디렉토리(전이적)에서 잎인 모든 하위 디렉토리, 즉 자체 하위 디렉토리가 없는 하위 디렉토리를 찾는 방법입니다. 예를 들어 다음과 같은 계층 구조가 있다고 가정합니다.

foo/
foo/bar/
foo/bar/baz
foo/you_fool

인수를 사용하여 호출 하면 내 가상 함수는 "foo"list 를 반환해야 합니다 ("foo/bar/baz/", "foo/you_fool/").

File::Find이는 분명히 또는 이와 동등한 것이 필요 하고 이미 stat찾은 모든 파일에 대해 시스템 호출을 수행하므로,빠르게각 파일에 대해 추가 작업을 수행 stat하더라도 각 파일에 대해 다른 작업을 수행 하지 않음을 의미합니다 .stat목차, 즉, 값이 $File::Find::dir괜찮습니다.

내 주요 대상 시스템은 Darwin(일명 MacOS)이므로 불행히도 ; nlink필드 를 사용할 수 없습니다 struct stat. 해당 파일 시스템에서는 의미가 없는 것 같습니다. 나는 "실제 Unix" 파일 시스템에서 nlink각 디렉토리를 2와 비교할 수 있다는 것을 알고 있습니다.

중요한 경우 심볼릭 링크, 특수 파일 및 기타 모든 이상한 항목을 무시할 수 있습니다. 검색할 계층 구조는 매우 깨끗하고 규칙적입니다.

답변1

다음을 수행할 수 있습니다.

perl -MFile::Find -le '
  find(sub { 
         if (-d _) {
           undef $leaves{$File::Find::name};
           delete $leaves{$File::Find::dir};
         }
       }, ".");
  print for keys %leaves'

undef현재 디렉터리의 해시 요소를 undef값으로 설정하고 delete상위 디렉터리의 해시 요소를 제거합니다. 따라서 최종 해시 키에는 %leaves리프만 포함됩니다.

의 경우 현재 파일에서 수행된 정보를 재사용하므로 -d _추가 / 작업이 수행되지 않습니다. 단독으로 추가(not)가 수행됩니다. 즉, 디렉토리에 대한 심볼릭 링크에 대해서도 true를 반환합니다.lstat()File::Findlstat()stat()-dstat()lstat()

테스트에서는 효과가 있었지만 효율적이고 미래 보장형이 아닐 수도 있습니다. 문서에는 다음과 같이 나와 있습니다.

["follow" 포함] 보장됨통계자료사용자의 "wanted()" 함수를 호출하기 전에 호출되었습니다. 이를 통해 "_" 관련 파일을 빠르게 확인할 수 있습니다.다음과 같은 경우에는 이 보증이 더 이상 유효하지 않습니다.따르다또는빠른 팔로우설정 없음.

이는 더 안전할 수 있지만 각 파일에 대해 추가 작업을 수행하는 if (! -l && -d _)비용이 듭니다 .lstat()

답변2

그냥 몇 가지 생각. 나는 펄 전문가가 아니기 때문에 File::Find가 무엇을 할 수 있는지 확신하지 못했기 때문에 쉘 '찾기'로 전환했습니다.

find / -type d -print

"/"로 시작하는 디렉토리 목록을 인쇄하므로 이것이 기본 목록입니다. C 응용 프로그램은 가능하지만 Perl을 더 빠르게 만들 수 있을지는 매우 의심됩니다. 사소한 이익을 위해 에너지를 낭비하는 것이 아닐까 의심됩니다.

GNU find에는 상위 디렉토리를 인쇄하기 위해 "%h" 플래그를 허용하는 "-printf" 옵션이 있습니다. 따라서 당신이 할 수 있는 일은 %p 경로와 상위 경로 %h를 동시에 -printf한 다음 상위 경로를 Perl의 새 목록으로 분할하는 것입니다. 이제 나뭇잎이 아닌 경로 목록이 있으므로 %p 목록에서 해당 경로를 제거하면 작업이 완료됩니다.

불행하게도 MacOS용 GNU 버전은 없고 더 낮은 버전만 있습니다. 'brew'를 사용하여 GNU find를 설치할 수 있지만 %p 행에서 Perl로 직접 %h 효과를 생성하는 것은 그리 어렵지 않습니다.

마지막으로 참고할 사항입니다. 파이프나 유사한 경로 이름의 개행 종료에 의존하면 경우에 따라 오류가 발생하는 것으로 알려져 있으므로 GNU find와 MacOS find는 모두 \n 대신 \0으로 구분된 행에 대해 제로 종료 옵션을 제공합니다. 사용할 수 있다면 그렇게 하세요.

답변3

File::Find제가 몰랐던, 잊어버렸던 기능들을 활용하니 생각보다 훨씬 간단하더군요 . 다음은 전체 스크립트입니다(질문과 관련 없는 코드를 추가하기 전).

#! /usr/bin/env perl

use warnings;
use strict;
use File::Find;
use Cwd qw(realpath);

@main::leaves = ();

sub preprocess {
  our (@leaves);
  my @names = @_;
  my @subdirs = grep { $_ ne q(.) && $_ ne q(..) && -d } @names;
  push @leaves, $File::Find::dir unless @subdirs;
  return @subdirs;
}

sub wanted {
  # do nothing at all
}

sub find_leaves {
  my @roots = map { realpath($_) } @ARGV;
  find({ wanted => \&wanted, preprocess => \&preprocess }, @roots);
}

sub main {
  our (@leaves);
  @ARGV or push @ARGV, q(.);
  find_leaves();
  print $_, qq(\n) foreach (@leaves);
  # my $num_leaves = $#leaves + 1;
}

main();
__END__

관련 정보