Unix는 여러 가지 자두와 유형 f에 압도됩니까?

Unix는 여러 가지 자두와 유형 f에 압도됩니까?

나는 이 루빅스 큐브에서 며칠을 보냈습니다. 하나의 문제를 해결하기 위해 내가 하는 모든 일은 다른 문제를 깨뜨립니다.

저는 POSIX 호환 MacOS X 10.5부터 10.14까지를 사용하고 있습니다. 나는 이것을 Perl 스크립트에서 다음과 같은 맥락으로 호출하고 있습니다.

  system ("find blah blah > FILENAME");

이 모든 일을 한 번에 수행하려면 Unix '찾기'가 필요합니다.

  • 볼륨 루트에서 시작합니다. 예:/Volumes/My HD
  • 파일 시스템을 교차하지 마십시오
  • 디렉토리나 심볼릭 링크가 아닌 파일만 인쇄
  • 내려가지도 마다수의. net dev system​(즉, /Volumes/foo/dev/를 탐색하지 말고 /Volumes/foo/Users/Jim/을 탐색하십시오.개발자/github/twonky/)
  • 시작점에는 공백이 포함될 수 있습니다.

지금은 다음을 수행하고 있습니다. (가독성을 위해 여러 줄로 나누어져 있지만 실제로는 긴 줄입니다.)

 Find -x '/Volumes/foo/' 
    -path '/Volumes/foo//dev/*' -prune
    -path '/Volumes/foo//net/*' -prune
    -path '/Volumes/foo//system/*' -prune
    -o -type f -print

이중의 이유는 /찾다시작점이 /로 끝나기 때문에 의 인쇄물에는 //가 포함됩니다. 정리 경로는 일치해야 합니다. 그렇지 않으면 일치하지 않습니다. 시작점이 /로 끝나는 이유는 무엇입니까? 왜냐면 그렇지 않다면,찾다"My HD"와 같이 이름에 공백이 있는 시작점에서는 실패합니다. 그것을 시도했습니다.

현재 find는 목록의 첫 번째 디렉토리만 제외합니다. 나머지는 그냥 무시합니다. 현재 OS X 10.5에서 테스트 중이지만 어디에서나 작동하는 것이 필요합니다.

여러 가지 정리 + 파일만 + 파일 이름의 공백이 너무 먼가요? 내가 너무 많은 것을 요구하는 걸까?찾다?

답변1

두 번째 일치를 수행하려면 "or"가 필요합니다. 두 가지 모두와 일치하는 단일 경로는 없습니다 -path '/Volumes/foo//dev/*'.-path '/Volumes/foo//net/*'

Find -x '/Volumes/foo/' 
    \( -path '/Volumes/foo//dev/*' 
    -o -path '/Volumes/foo//net/*' 
    -o -path '/Volumes/foo//system/*' \) -prune
-o -type f -print

답변2

순수한 Perl 솔루션에 대한 나의 대답입니다.

이 샌드박스를 사용하면 다음과 같습니다.

$ tree -F Volumes/ 
Volumes/ 
└── My\ HD/
    ├── Users/
    │   └── Jim/
    │       └── dev/
    │           └── github/
    │               └── twonky/
    │                   └── i_there.txt
    ├── dev/
    ├── net/
    ├── start.bat
    └── system/
        └── hello

9 directories, 3 files

다음 Perl 코드는 다음을 사용합니다 File::Find.

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use File::Find;

my $start = 'Volumes/My HD';
my $start_dev = (stat($start))[0];
my @exclude = qw/net dev system/;
my %skipdir;

sub wanted {
    my $name = $_;
    return if (stat($name))[0] != $start_dev;
    $skipdir{$File::Find::name} = 1 if $File::Find::dir eq $start && grep { $name eq $_ } @exclude;
    if (exists($skipdir{$File::Find::dir})) {
        $skipdir{$File::Find::name} = 1 if -d $name;
        return;
    }
    return if ! -f $name;
    say "Got: $File::Find::name";

}

my %args = (
    wanted => \&wanted,
    follow => 1,
    follow_skip => 1,
);


find(\%args, $start);

예상되는 결과를 제공합니다(내가 이해한 것이 맞다면):

Got: Volumes/My HD/start.bat
Got: Volumes/My HD/Users/Jim/dev/github/twonky/i_there.txt

POC이므로 향상될 수 있습니다.

또한 동일한 기준을 사용하여 특정 호출을 관련 Perl 코드로 find2perl변환할 수 있도록 문서화된 도구 가 있다는 점에 유의하십시오 .findFile::Find

이제 Path::Class코드가 더 간단하고 읽기 쉬워 보일 수 있습니다(동일한 결과에 대해).

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use Path::Class;

my $start = Path::Class::Dir->new('Volumes/My HD');
my @exclude = qw/net dev system/;

$start->recurse(callback => sub {
    my $node = shift;
    if ($node->is_dir) {
        return $node->PRUNE if $node->parent eq $start && grep { $node->dir_list(-1) eq $_ } @exclude;
        return;
    }
    return $node->PRUNE if $node->stat()->dev != $start->stat()->dev;
    say 'Got: ', $node->stringify();
}, preorder => 1)

답변3

여러분의 도움으로 '찾기'를 안정시킬 수 있었습니다. 그러나 OS X 10.5에서 10.10으로 코드 이동또 망쳤어. 그것은 마지막 빨대였습니다. '찾기'는 너무 모호하고 문서화가 부족하며 일관성이 없으며 Pete를 위한 유닉스 핵심 기능입니다! 이것. 이래서 나는 남의 코드를 싫어한다. 나는 File::Find를 배우기 위해 몸을 숙이기 시작했고 "나는 무엇인가?"라고 생각했습니다.행위? 이건 내가 직접 코딩할 수 있어20분 안에."

나는 요약해서 그랬다.

sub iterate {
  my ($mydir, $ref_FH, $homevol, $ref_excludes) = @_;  # last is ref to hash

  return if (defined ($ref_excludes -> {$mydir}));   # No excludes

  my $thisvol = (stat($mydir))[0];    # What's my volume?
  return if ($thisvol != $homevol) ;  # No crossing volumes

  opendir (my $DIR, $mydir);
  while (defined (my $file = readdir($DIR))) {
    next if ($file eq '.' or $file eq '..');
    my $full = "$mydir/$file";   

    if (-l $full) {                                   # symlink
                                                         # nope
    } elsif (-f $full) {                              # file
      print {$$ref_FH} "$full\n";                        # print it
    } elsif (-d $full) {                              # dir
      &iterate($full, $ref_FH, $homevol, $ref_excludes); # iterate
    }
  }
}

그리고 그것은 빠릅니다. 그리고 가볍습니다. 이 코드는 "find"의 인수 목록 형식을 지정하는 코드보다 크기가 절반이고 유지 관리가 더 쉽습니다!

관련 정보