¿Qué razones existen para preferir glob sobre readdir (o viceversa) en Perl?

Esta pregunta es un spin-off de este . Algo de historia: cuando aprendí Perl por primera vez, casi siempre usaba glob lugar de opendir + readdir porque me resultaba más fácil. Luego, más tarde, varios mensajes y lecturas sugirieron que glob era malo, por lo que ahora casi siempre uso readdir .

Después de pensar en esta pregunta reciente, me di cuenta de que mis razones para una u otra opción pueden ser litera. Por lo tanto, voy a exponer algunos pros y contras, y espero que la gente más experimentada de Perl pueda intervenir y aclarar. La pregunta en pocas palabras es ¿existen razones de peso para preferir glob a readdir o readdir a glob (en algunos o todos los casos)?

Pros glob :

  1. No hay archivos de puntos (a menos que los solicite)
  2. El orden de los artículos está garantizado
  3. No es necesario anteponer el nombre del directorio a los elementos manualmente
  4. Mejor nombre (c’mon – glob versus readdir no es competencia si estamos juzgando solo por nombres)
  5. (A partir de la respuesta de ysth; cf. glob cons 4 a continuación) Puede devolver nombres de archivo inexistentes:

     @deck = glob "{A,K,Q,J,10,9,8,7,6,5,4,3,2}{\x{2660},\x{2665},\x{2666},\x{2663}}"; 

contras glob :

  1. Las versiones antiguas simplemente están rotas (pero ‘más antiguo’ significa pre 5.6, creo, y francamente si estás usando 5.6 Perl anterior, tienes problemas mayores)
  2. Llamada stat cada vez (es decir, uso inútil de stat en la mayoría de los casos).
  3. Problemas con espacios en nombres de directorio (¿esto sigue siendo cierto?)
  4. (De la respuesta de Brian) Puede devolver nombres de archivo que no existen:

     $ perl -le 'print glob "{ab}{cd}"' 

readdir pros:

  1. (De la respuesta de opendir ) opendir devuelve un opendir que puede pasar en su progtwig (y reutilizar), pero glob simplemente devuelve una lista
  2. (De la respuesta de readdir ) readdir es un iterador apropiado y proporciona funciones para rewinddir , seekdir , telldir
  3. ¿Más rápido? (Conjetura pura basada en algunas de las características de glob de arriba. No estoy realmente preocupado por este nivel de optimización de todos modos, pero es un profesional teórico).
  4. ¿Menos propenso a los errores de borde que a glob ?
  5. Lee todo (archivos de puntos también) de manera predeterminada (esto también es una estafa)
  6. Puede convencerlo de que no nombre un archivo 0 (una estafa también – vea la respuesta de Brad)
  7. ¿Nadie? Bueller? Bueller?

readdir contras:

  1. Si no recuerda agregar el nombre del directorio, recibirá un bit cuando intente realizar pruebas de archivos o copiar elementos o editar elementos o …
  2. Si no recuerdas agotar el . y .. elementos, obtendrá un poco cuando cuente elementos, o intente caminar recursivamente por el árbol de archivos o …
  3. ¿Mencioné anteponer el nombre del directorio? (Una nota al margen, pero mi primera publicación en la lista de correo de Perl para principiantes fue el clásico “¿Por qué este código que involucra pruebas de archivos no funciona alguna vez?” Relacionado con este problema. Aparentemente, todavía estoy amargado).
  4. Los artículos son devueltos sin un orden en particular. Esto significa que a menudo tendrá que recordar ordenarlos de alguna manera. (Esto podría ser un profesional si significa más velocidad, y si eso significa que realmente piensa en cómo y si necesita ordenar elementos). Editar : Muestra pequeña y readdir , pero en un readdir Mac devuelve los elementos en orden alfabético, sin distinción entre mayúsculas y minúsculas. . En una caja Debian y un servidor OpenBSD, el orden es completamente aleatorio. Probé la Mac con el Perl integrado de Apple (5.8.8) y mi propio comstackdo 5.10.1. El cuadro de Debian es 5.10.0, al igual que el equipo de OpenBSD. Me pregunto si esto es un problema del sistema de archivos, en lugar de Perl.
  5. Lee todo (archivos duales también) de forma predeterminada (esto también es un profesional)
  6. No necesariamente funciona bien con un archivo llamado 0 (ver pros también – ver la respuesta de Brad)

Te perdiste la diferencia más grande y más importante entre ellos: glob te devuelve una lista, pero opendir te da un identificador de directorio. Puede pasar ese control de directorio para permitir que otros objetos o subrutinas lo usen. Con el identificador de directorio, la subrutina u objeto no tiene que saber nada acerca de su origen, quién más lo está usando, y así sucesivamente:

  sub use_any_dir_handle { my( $dh ) = @_; rewinddir $dh; ...do some filtering... return \@files; } 

Con el manejador de dir, tienes un iterador controlable donde puedes moverte con seekdir , aunque con glob simplemente obtienes el siguiente elemento.

Sin embargo, al igual que con cualquier cosa, los costos y beneficios solo tienen sentido cuando se aplican a un contexto determinado. No existen fuera de un uso particular. Tienes una excelente lista de sus diferencias, pero no clasificaría esas diferencias sin saber lo que intentabas hacer con ellas.

Algunas otras cosas para recordar:

  • Puedes implementar tu propio glob con opendir , pero no al revés.

  • glob usa su propia syntax comodín, y eso es todo lo que obtienes.

  • glob puede devolver nombres de archivos que no existen:

     $ perl -le 'print glob "{ab}{cd}"' 

global pros: puede devolver ‘nombres de archivo’ que no existen:

 my @deck = List::Util::shuffle glob "{A,K,Q,J,10,9,8,7,6,5,4,3,2}{\x{2660},\x{2665},\x{2666},\x{2663}}"; while (my @hand = splice @deck,0,13) { say join ",", @hand; } __END__ 6♥,8♠,7♠,Q♠,K♣,Q♦,A♣,3♦,6♦,5♥,10♣,Q♣,2♠ 2♥,2♣,K♥,A♥,8♦,6♠,8♣,10♠,10♥,5♣,3♥,Q♥,K♦ 5♠,5♦,J♣,J♥,J♦,9♠,2♦,8♥,9♣,4♥,10♦,6♣,3♠ 3♣,A♦,K♠,4♦,7♣,4♣,A♠,4♠,7♥,J♠,9♥,7♦,9♦ 

Aquí hay una desventaja para opendir y readdir .

 { open my $file, '>', 0; print {$file} 'Breaks while( readdir ){ ... }' } opendir my $dir, '.'; my $a = 0; ++$a for readdir $dir; print $a, "\n"; rewinddir $dir; my $b = 0; ++$b while readdir $dir; print $b, "\n"; 

Es de esperar que ese código imprima el mismo número dos veces, pero no porque haya un archivo con el nombre 0 . En mi computadora imprime 251 y 188 , probado con Perl v5.10.0 y v5.10.1

Este problema también hace que esto solo imprima un montón de líneas vacías, independientemente de la existencia del archivo 0 :

 use 5.10.0; opendir my $dir, '.'; say while readdir $dir; 

Donde como esto siempre funciona bien:

 use 5.10.0; my $a = 0; ++$a for glob '*'; say $a; my $b = 0; ++$b while glob '*'; say $b; say for glob '*'; say while glob '*'; 

Repare estos problemas y envié un parche que lo convirtió en Perl v5.11.2, por lo que funcionará correctamente con Perl v5.12.0 cuando se publique.

Mi solución convierte esto:

 while( readdir $dir ){ ... } 

dentro de esto:

 while( defined( $_ = readdir $dir ){ ...} 

Lo que hace que funcione de la misma manera que la read ha trabajado en los archivos. En realidad, es el mismo fragmento de código, acabo de agregar otro elemento a las sentencias if correspondientes.

glob hace que sea conveniente leer todos los subdirectorios de una profundidad fija dada, como en glob "*/*/*" . Lo he encontrado útil en varias ocasiones.

Bueno, prácticamente lo cubres. Teniendo esto en cuenta, tendería a usar glob cuando glob un guión único y rápido y su comportamiento es justo lo que quiero, y use opendir y readdir en el código de producción en curso o en las bibliotecas donde puedo tomarme mi tiempo y un código más claro y limpio es útil.

Para cosas pequeñas y simples, prefiero glob . Justo el otro día, lo usé y una secuencia de comandos de veinte líneas de Perl para volver a grabar una gran parte de mi biblioteca de música. glob , sin embargo, tiene un nombre bastante extraño. Glob? No es para nada intuitivo, por lo que se refiere a un nombre.

Mi mayor problema con readdir es que trata un directorio de una manera extraña para la mayoría de las personas. Por lo general, los progtwigdores no piensan en un directorio como un flujo, lo consideran un recurso, o una lista, lo que proporciona glob. El nombre es mejor, la funcionalidad es mejor, pero la interfaz aún deja algo que desear.

Esa fue una lista bastante completa. readdir (y readdir + grep ) tiene menos sobrecarga que glob por lo que es una ventaja para readdir si necesita analizar muchos directorios.

Pros globales:

3) No es necesario anteponer el nombre del directorio a los elementos manualmente

Excepción:

 say for glob "*"; --output:-- 1perl.pl 2perl.pl 2perl.pl.bak 3perl.pl 3perl.pl.bak 4perl.pl data.txt data1.txt data2.txt data2.txt.out 

Por lo que puedo decir, la regla para glob es: debe proporcionar una ruta completa al directorio para obtener caminos completos hacia atrás. Los documentos de Perl no parecen mencionar eso, y ninguno de los mensajes aquí.

Esto significa que se puede usar glob en lugar de readdir cuando solo desea nombres de archivo (en lugar de rutas completas), y no desea que se devuelvan archivos ocultos, es decir, aquellos que comienzan con ‘.’. Por ejemplo,

 chdir ("../.."); say for glob("*"); 

En una nota similar, File::Slurp tiene una función llamada read_dir .

Como utilizo mucho las otras funciones de File::Slurp en mis scripts, read_dir también se ha convertido en un hábito.

También tiene las siguientes opciones: err_mode , prefix y keep_dot_dot .

Primero, lee un poco. Capítulo 9.6. de Perl Cookbook describe el punto que quiero llegar muy bien, justo debajo del encabezado de la discusión.

En segundo lugar, haga una búsqueda de glob y dosglob en su directorio de Perl. Si bien se pueden usar muchas fonts diferentes (formas de obtener la lista de archivos), la razón por la que le dosglob es que si está en una plataforma Windows (y usa la solución dosglob ), en realidad está usando opendir / readdir / closedir . Otras versiones usan comandos de shell incorporados o ejecutables precomstackdos específicos del sistema operativo.

Si sabe que se dirige a una plataforma específica, puede usar esta información para su beneficio. Solo como referencia, analicé esto en Strawberry Perl Portable edition 5.12.2, por lo que las cosas pueden ser ligeramente diferentes en versiones más nuevas o originales de Perl.