
Ich habe Tage mit diesem Zauberwürfel verbracht. Alles, was ich tue, um ein Problem zu lösen, führt zu einem anderen.
Ich verwende POSIX-kompatibles MacOS X 10.5 bis 10.14. Ich rufe dies aus einem Perl-Skript im Kontext von
system ("find blah blah > FILENAME");
Ich brauche Unix „find“, um all diese Dinge gleichzeitig zu tun.
- Beginnen Sie bei einer Lautstärkenwurzel, z. B.
/Volumes/My HD
- nicht über Dateisysteme hinweg
- nur Dateien drucken, keine Verzeichnisse oder symbolische Links
- nicht einmal hinabsteigen inmehrereVerzeichnisse wie
net dev system
. (D. h., erkunden Sie nicht /Volumes/foo/dev/, sondern /Volumes/foo/Users/Jim/Entwickler/github/twonky/) - Der Startpunkt kann Leerzeichen enthalten
Im Moment mache ich Folgendes: (zur besseren Lesbarkeit in mehrere Zeilen aufgeteilt, eigentlich ist es eine lange Zeile)
Find -x '/Volumes/foo/'
-path '/Volumes/foo//dev/*' -prune
-path '/Volumes/foo//net/*' -prune
-path '/Volumes/foo//system/*' -prune
-o -type f -print
Der Grund für das doppelte / istfindenDer Ausdruck von enthält das //, weil der Startpunkt mit einem / endet. Die Prune-Pfade müssen übereinstimmen, sonst stimmen sie nicht überein. Warum endet der Startpunkt mit /?? Denn wenn nicht,findenschlägt bei jedem Startpunkt mit einem Leerzeichen im Namen fehl, wie etwa „Meine Festplatte“. Das habe ich versucht.
Im Moment schließt find nur das erste Verzeichnis in der Liste aus. Der Rest wird einfach ignoriert. Ich teste derzeit unter OS X 10.5, aber ich brauche etwas, das überall funktioniert.
Sind mehrere Beschneidungen + nur Dateien + Leerzeichen in Dateinamen zu viel verlangt? Verlange ich einfach zu viel vonfinden?
Antwort1
Sie benötigen ein „oder“, um die zweite Übereinstimmung zu erreichen – kein einzelner Pfad wird sowohl als auch als -path '/Volumes/foo//dev/*'
übereinstimmen-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
Antwort2
Meine Antwort mit einer reinen Perl-Lösung.
Mit dieser Sandbox:
$ tree -F Volumes/
Volumes/
└── My\ HD/
├── Users/
│ └── Jim/
│ └── dev/
│ └── github/
│ └── twonky/
│ └── i_there.txt
├── dev/
├── net/
├── start.bat
└── system/
└── hello
9 directories, 3 files
Der folgende Perl-Code verwendet 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);
gibt das erwartete (wenn ich Sie richtig verstanden habe):
Got: Volumes/My HD/start.bat
Got: Volumes/My HD/Users/Jim/dev/github/twonky/i_there.txt
Es handelt sich um einen POC, er kann erweitert werden.
Beachten Sie auch, dass Sie über das find2perl
dokumentierte Tool verfügen, um einen bestimmten Aufruf unter Verwendung derselben Kriterien find
in den zugehörigen Perl-Code konvertieren zu können.File::Find
Nun Path::Class
scheint der Code einfacher/leichter lesbar zu sein (bei gleichem Ergebnis):
#!/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)
Antwort3
Mit Ihrer Hilfe konnte ich "find" stabilisieren. Allerdings wurde der Code von OS X 10.5 auf 10.10 verschoben.hat es wieder kaputt gemacht. Das war der Tropfen, der das Fass zum Überlaufen brachte. 'find' ist einfach zu stumpfsinnig, unzureichend dokumentiert und inkonsistent, und es ist um Himmels Willen ein Unix-Kernfeature! Das. Das ist der Grund, warum ich den Code anderer Leute hasse. Ich fing an, mich mit File::Find zu beschäftigen, dachte dann aber: "Was mache ichmache ich? Ich kann das selbst codierenin 20 Minuten".
Was ich kurzerhand tat.
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
}
}
}
Und es ist schnell. Und leicht – dieser Code ist halb so groß (und wartungsfreundlicher) als der Code, der die Argumentliste von „find“ formatiert hat!