¿Unix se encuentra abrumado por múltiples ciruelas pasas y tipo f?

¿Unix se encuentra abrumado por múltiples ciruelas pasas y tipo f?

He pasado días en este cubo de Rubik. Cualquier cosa que haga para solucionar un problema arruina otro.

Estoy en MacOS X 10.5 a 10.14 compatible con POSIX. Estoy llamando a esto desde un script Perl en un contexto de

  system ("find blah blah > FILENAME");

Necesito "buscar" de Unix para hacer todas estas cosas a la vez.

  • comenzar en la raíz de un volumen, por ejemplo/Volumes/My HD
  • no cruzar sistemas de archivos
  • imprimir solo archivos, no directorios ni enlaces simbólicos
  • ni siquiera desciendasmúltipledirectorios como net dev system. (Es decir, no explore /Volumes/foo/dev/ pero sí explore /Volumes/foo/Users/Jim/desarrollador/github/twonky/)
  • el punto de inicio puede contener espacios

En este momento estoy haciendo lo siguiente: (dividido en varias líneas para facilitar la lectura; en realidad es una línea larga)

 Find -x '/Volumes/foo/' 
    -path '/Volumes/foo//dev/*' -prune
    -path '/Volumes/foo//net/*' -prune
    -path '/Volumes/foo//system/*' -prune
    -o -type f -print

La razón del doble / esencontrarLa impresión incluye // porque el punto inicial termina en /. Las rutas de Prune deben coincidir o no coincidirán. ¿Por qué el punto de partida termina en /? Porque si no es así,encontrarfalla en cualquier punto de partida con un espacio en el nombre, como "Mi HD". Lo intenté.

En este momento, buscar solo excluye el primer directorio de la lista. El resto simplemente lo ignora. Actualmente estoy realizando pruebas en OS X 10.5 pero necesito algo que funcione en todas partes.

¿Múltiples podas + solo archivos + espacios en los nombres de archivos son un puente demasiado lejos? ¿Estoy pidiendo demasiado?encontrar?

Respuesta1

Necesita una "o" para lograr la segunda coincidencia: ninguna ruta coincidirá con ambas -path '/Volumes/foo//dev/*'y-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

Respuesta2

Mi respuesta con una solución Perl pura.

Con esta caja de arena:

$ tree -F Volumes/ 
Volumes/ 
└── My\ HD/
    ├── Users/
    │   └── Jim/
    │       └── dev/
    │           └── github/
    │               └── twonky/
    │                   └── i_there.txt
    ├── dev/
    ├── net/
    ├── start.bat
    └── system/
        └── hello

9 directories, 3 files

El siguiente código Perl usando 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);

da lo esperado (si te entendí bien):

Got: Volumes/My HD/start.bat
Got: Volumes/My HD/Users/Jim/dev/github/twonky/i_there.txt

Es un POC, se puede mejorar.

Tenga en cuenta también que tiene la find2perlherramienta documentada para poder convertir una findinvocación específica al código Perl asociado utilizando File::Findel mismo criterio.

Ahora Path::Classel código puede parecer más simple/fácil de leer (para el mismo resultado):

#!/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)

Respuesta3

Con tu ayuda, pude estabilizar "buscar". Sin embargo, mover el código de OS X 10.5 a 10.10lo rompió de nuevo. Eso fue el colmo. 'buscar' es simplemente demasiado obtuso, poco documentado e inconsistente, ¡y es una característica central de Unix, por el amor de Dios! Este. Por eso odio el código de otras personas. Empecé a agacharme para aprender File::Find, luego pensé "¿qué soy?"¿haciendo? Puedo codificar esto yo mismoen 20 minutos".

Lo cual hice sumariamente.

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
    }
  }
}

Y es rápido. Y ligero: ¡este código tiene la mitad del tamaño (y es más fácil de mantener) que el código que formateó la lista de argumentos de "buscar"!

información relacionada