Не перегружается ли поиск Unix множеством сокращений и вводом f?

Справочная страница для bash указывает:

Расширение имени пути

После разделения слов, если не установлен параметр -f, bash сканирует каждое слово на наличие символов *, ? и [. Если появляется один из этих символов, то слово рассматривается как шаблон и заменяется отсортированным по алфавиту списком имен файлов, соответствующих шаблону […].

0
10.06.2019, 18:26
3 ответа

Мой ответ с решением на чистом 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)
2
28.01.2020, 02:14

С вашей помощью мне удалось стабилизировать "находку". Однако перенос кода с 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"!

1
28.01.2020, 02:14

Вам нужно "или" для выполнения второго совпадения --ни один путь не будет соответствовать и -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
5
28.01.2020, 02:14

Теги

Похожие вопросы