Справочная страница для bash указывает:
Расширение имени пути
После разделения слов, если не установлен параметр
-f
, bash сканирует каждое слово на наличие символов*
,?
и[
. Если появляется один из этих символов, то слово рассматривается как шаблон и заменяется отсортированным по алфавиту списком имен файлов, соответствующих шаблону […].
Мой ответ с решением на чистом 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)
С вашей помощью мне удалось стабилизировать "находку". Однако перенос кода с OS X 10.5 на 10.10 снова сломал его . Это была последняя капля. «найти» просто слишком тупой, недостаточно документированный и непоследовательный,и это основная функция 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"!
Вам нужно "или" для выполнения второго совпадения --ни один путь не будет соответствовать и -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