
我在這個魔術方塊上花了好幾天的時間。我為解決一個問題所做的任何事情都會破壞另一個問題。
我使用的是 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/) - 起點可能包含空格
現在我正在執行以下操作:(為了可讀性分成幾行;它實際上是一長行)
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”。試過了。
目前,尋找僅排除清單中的第一個目錄。其餘的,它只是忽略。我目前正在 OS X 10.5 上進行測試,但我需要一些可以在任何地方使用的東西。
多個修剪+僅檔案+檔案名稱中的空格是一個橋樑太遠了嗎?我是不是要求太多了尋找?
答案1
您需要一個“或”來完成第二個匹配 - 沒有一個路徑可以同時匹配-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,它是可以增強的。
另請注意,您擁有find2perl
記錄為能夠使用相同條件將特定find
呼叫轉換為關聯 Perl 程式碼的工具。File::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又把它打破了。最後的機會。 「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」的 arg 清單的程式碼的一半(並且更易於維護)!