Expansión de nombre de archivo: Buscar coincidencia de patrones de utilidad vs Coincidencia de patrones de Bash Shell

6

Para la expansión del nombre de archivo, la opción '-name' de la utilidad 'find' parece funcionar de manera similar, pero no exactamente igual a la coincidencia de patrones incorporados del shell bash.

Aquí están las secciones relevantes del manual de referencia de GNU:

Esto es muy confuso por sí solo. Para agregar a esta confusión, la sección 2.1.4 de la página de manual de la utilidad 'find' (mencionada anteriormente) se titula "Coincidencia de patrones de shell", lo que implica que 'find' está utilizando la funcionalidad de coincidencia de patrones incorporada de shell. Sin embargo, este no parece ser el caso porque de acuerdo con la página del manual 'find' ( http://goo.gl/ngQTKx ), bajo '-name pattern', dice lo siguiente:

"La coincidencia del nombre de archivo se realiza con el uso de la función de biblioteca fnmatch (3). No olvides encerrar el patrón entre comillas para protegerlo de la expansión del shell".

A partir de esto, parece que no es el shell el que realiza la coincidencia de patrones, sino la utilidad de búsqueda que usa la biblioteca fnmatch.

Aquí están mis preguntas:

  1. ¿La expansión del nombre de archivo predeterminado de bash shell y la coincidencia de patrones (opción de shell extglob deshabilitada) es diferente de la de la utilidad find usando la opción -name?
  2. Si es así, ¿cuáles son esas diferencias?
  3. ¿Bash también usa la biblioteca fnmatch o algún otro mecanismo para la expansión del nombre de archivo y la coincidencia de patrones?
teancum144
fuente

Respuestas:

6

En el shell, debe distinguir la generación / expansión de nombre de archivo (también conocido como globbing : un patrón que se expande a una lista de archivos) de la coincidencia de patrones . globbing utiliza la coincidencia de patrones internamente , pero en realidad es ante todo un operador generar una lista de archivos basada en un patrón .

*/*.txtes un patrón que coincide con una secuencia de 0 o más caracteres, seguido de /, seguido de una secuencia de cero o más caracteres, seguido de .txt. Cuando se usa como un patrón de shell como en:

case $file in
  */*.txt) echo match
esac

Coincidirá con file=.foo/bar/baz.txt.

Sin embargo, */*.txtcomo glob es algo relacionado pero más complejo.

Al expandirse */*.txta una lista de archivos, el shell abrirá el directorio actual, enumerará su contenido, encontrará los archivos no ocultos de tipo directorio (o enlace simbólico al directorio) que coincidan *, clasifique esa lista, abra cada uno de ellos, enumere su contenido , y encuentre los que no están ocultos que coinciden *.txt.

Nunca se expandirá .foo/bar/bar.txtaunque coincida con el patrón porque no es así como funciona. Por otro lado, todas las rutas de archivos generadas por un glob coincidirán con ese patrón.

Del mismo modo, un glob like foo[a/b]baz*encontrará todos los archivos cuyo nombre comienza b]bazen el foo[adirectorio.

Por lo tanto, ya lo hemos visto para el engorde, pero no para la coincidencia de patrones, /es especial (los globos se dividen de alguna manera /y cada parte se trata por separado) y los archivos de puntos se tratan especialmente.

El globbing de shell y la coincidencia de patrones son parte de la sintaxis de shell Está entrelazado con citas y otras formas de expansión.

$ bash -c 'case "]" in [x"]"]) echo true; esac'
true

Cita que ]elimina su significado especial (de cerrar el anterior [):

Puede ser aún más confuso cuando mezclas todo:

$ ls
*  \*  \a  x

$ p='\*' ksh -xc 'ls $p'
+ ls '\*' '\a'
\*  \a

OK \*son todos los archivos que comienzan con \.

$ p='\*' bash -xc 'ls $p'
+ ls '\*'
\*

No todos los archivos comienzan con \. Entonces, de alguna manera, \debe haber escapado del *, pero, de nuevo, tampoco coincide *...

Para encontrar, es mucho más simple. finddesciende el árbol de directorios en cada uno de los argumentos de archivo que recibe y luego realiza las pruebas como se indica para cada archivo encontrado.

Para -type f, eso es cierto si el archivo es un archivo normal, falso de lo contrario para -name <some-pattern>, eso es cierto si el nombre del archivo actualmente considerado coincide con el patrón, falso de lo contrario. No hay ningún concepto de archivo oculto o /manejo o citación de shell aquí, eso solo coincide con una cadena (el nombre del archivo) contra un patrón.

Entonces, por ejemplo, -name '*foo[a/b]ar'(que pasa -namey *foo[a/b]arargumentos a find) coincidirá foobary .fooaar. Nunca coincidirá foo/bar, pero eso se debe a que -namecoincide con el nombre del archivo; lo haría con en su -pathlugar.

Ahora, hay una forma de citar / escapar - parafind - reconocida aquí, y eso es solo con barra invertida. Eso permite escapar de los operadores. Para el shell, se realiza como parte de las citas habituales de shell ( \es uno de los mecanismos de cotización del shell). Para find( fnmatch()), eso es parte de la sintaxis del patrón.

Por ejemplo, -name '\**'coincidiría en archivos cuyo nombre comience con *. -name '*[\^x]*'coincidiría en archivos cuyo nombre contiene ^o x...

Ahora, en cuanto a los diferentes operadores reconocidos por find, fnmatch(), bashy varios otros proyectiles, todos ellos deben estar de acuerdo al menos en un subconjunto común: *, ?y [...].

Si un shell o findimplementación particular utiliza la fnmatch()función del sistema o la suya depende de la implementación. GNU lo findhace al menos en sistemas GNU. Es muy poco probable que los proyectiles los usen, ya que les complicaría las cosas y no valdría la pena el esfuerzo.

bashCiertamente no lo hace. Conchas modernas, como ksh, bash, zsh también tienen extensiones sobre *, ?, [...]y una serie de opciones y parámetros especiales ( GLOBIGNORE/ FIGNORE) que afectan a su comportamiento comodines.

También tenga en cuenta que además de lo fnmatch()que implementa la coincidencia de patrones de shell, también existe la glob()función que implementa algo similar al globbing de shell.

Ahora, puede haber diferencias sutiles entre los operadores de coincidencia de patrones en esas diversas implementaciones.

Por ejemplo, para GNU fnmatch(), ?, *o [!x]que no coincide con un byte o una secuencia de bytes que no forman caracteres válidos, mientras que bash(y otros intérpretes de comandos) haría. Por ejemplo, en un sistema GNU, es find . -name '*'posible que no coincidan los archivos cuyo nombre contiene caracteres no válidos, mientras bash -c 'echo *'que los enumerará (siempre que no comiencen con .).

Ya hemos mencionado la confusión en la que se puede incurrir al citar.

Stéphane Chazelas
fuente
En la siguiente línea desde arriba, ¿qué hace el interruptor '-x'? $ p = '*' bash -xc 'ls $ p' No puedo encontrarlo en 'man bash'.
teancum144