私が開発している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 _
追加/操作は行われません。単独で追加が行われます。つまり、ディレクトリへのシンボリックリンクに対してもtrueを返します。lstat()
File::Find
lstat()
stat()
-d
stat()
lstat()
テストでは効果がありましたが、効率的で将来保証型ではない可能性があります。文書には次のように記載されています。
["follow"を含む]保証済み統計資料ユーザーの "wanted()" 関数を呼び出す前に呼び出されました。これにより、「_」関連ファイルをすばやく確認できます。次の場合、この保証は無効になります。フォローするまたはクイックフォロー設定なし。
これはより安全ですが、各ファイルに対して追加の操作を実行するのはif (! -l && -d _)
費用がかかります。lstat()
答え2
ちょうどいくつかの考え。私はPerlの専門家ではないので、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__