Unix の find は複数のプルーンとタイプ f によって圧倒されますか?

Unix の find は複数のプルーンとタイプ f によって圧倒されますか?

私はこのルービックキューブに何日も費やしました。一つの問題を解決するために何かをすると、別の問題が壊れてしまいます。

私はPOSIX準拠のMacOS X 10.5から10.14を使用しています。私はこれをPerlスクリプトから呼び出して、

  system ("find blah blah > FILENAME");

これらすべてのことを一度に実行するには、Unix の「find」が必要です。

  • ボリュームルートから開始します。例:/Volumes/My HD
  • ファイルシステムをまたがらない
  • ディレクトリやシンボリックリンクではなく、ファイルのみを印刷します
  • 降りることさえしない複数のようなディレクトリnet dev system。(つまり、/Volumes/foo/dev/ は探索しませんが、/Volumes/foo/Users/Jim/ は探索します。開発(/github/twonky/) より
  • 開始点にはスペースが含まれる場合があります

現在、私は次のことを行っています: (読みやすくするために複数の行に分割されていますが、実際には 1 つの長い行です)

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

二重の理由は探すのプリントアウトには // が含まれています。これは、開始点が / で終わっているためです。Prune パスは一致する必要があります。一致しないと一致しません。開始点が / で終わるのはなぜでしょうか。そうでない場合、探す「My HD」のように、名前にスペースが含まれる開始点では失敗します。試してみました。

現時点では、find はリストの最初のディレクトリのみを除外します。残りは無視されます。現在、OS X 10.5 でテストしていますが、どこでも機能するものが必要です。

複数のプルーン+ファイルのみ+ファイル名のスペースはやりすぎでしょうか?私が求めすぎているだけでしょうか?探す?

答え1

2番目の一致を達成するには「または」が必要です。単一のパスは両方に一致しません-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

あなたの助けにより、「find」を安定させることができました。しかし、コードをOS X 10.5から10.10に移行するとまた壊した。それが最後の一撃でした。「find」はあまりにもわかりにくく、ドキュメントも不十分で一貫性もありません。しかも、これはUNIXのコア機能です。これです。これが私が他人のコードが嫌いな理由です。私は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」の引数リストをフォーマットしたコードの半分のサイズです (そして、より保守しやすいです)。

関連情報