La expansión con * .txt en el shell no funciona si no existe un archivo .txt

10

Estaba jugando con la expansión y noté un comportamiento peculiar. Traté de hacer:

echo ./*.txt

Y no tenía ningún archivo .txt en mi directorio actual. El resultado que obtuve fue:

./*.txt

Solo tengo curiosidad: ¿por qué obtuve esto? Esperaba no obtener ningún resultado.

PD: Cuando tuve un .txtarchivo, la expansión se interpretó correctamente. En otras palabras, digamos que tenía un archivo smthn.txt, el eco realmente hizo eco current_directory/smthn.txt.

Un vagabundo ignorante
fuente

Respuestas:

15

Adaptación de la página de manual de bash shell,

bash escanea cada palabra para los caracteres *,? y [. Si aparece uno de estos caracteres, la palabra se considera un patrón y se reemplaza por una lista ordenada alfabéticamente de nombres de archivos que coinciden con el patrón. Si no se encuentran nombres de archivo coincidentes y la opción de shell nullglob no está habilitada, la palabra no se modifica. Si se establece la opción nullglob y no se encuentran coincidencias, la palabra se elimina.

En este caso, supongo que nullglob no está habilitado, por lo que la palabra no se modifica, de ahí el resultado que ve.

ColinB
fuente
2
Puede cambiar el comportamiento de la siguiente manera: shopt -s nullglobproducirá cadenas vacías para patrones no coincidentes y shopt -u nullglob(configuración estándar) producirá el patrón en sí.
PerlDuck
13

Esperaba no obtener ningún resultado.

Si nullglobfuera el valor predeterminado, muchos comandos se comportarían de manera bastante inesperada, porque es (quizás desafortunadamente) común que los comandos traten el caso de cero argumentos de nombre de archivo de una manera cualitativamente diferente que el caso de uno o más argumentos de nombre de archivo.

Supongamos que ha habilitado nullglob( shopt -s nullglob) y está en un directorio donde no coinciden los archivos *.txt. Entonces *.txt, de hecho, se expandirá a la nada, no a un campo vacío, sino a ningún campo, como esperaba. Pero eso tendría estos resultados:

  • ls *.txtenumeraría todos los archivos en el directorio actual (excepto los archivos ocultos), porque eso es lo que lshace cuando no le pasa ningún argumento de nombre de archivo.
  • cat *.txtleería de la entrada estándar , porque cuando catno tiene argumentos de nombre de archivo, es como si ejecutara cat -. Si se ejecuta de forma interactiva, se queda esperando la entrada. Muchos comandos se comportan de esta manera.
  • cp *.txt dest/fallaría con el error cp: missing destination file operand after 'dest/'. Esto no es un desastre, pero es confuso y bastante diferente del éxito silencioso que probablemente se desea.
  • file *.txt, y varios otros programas sin comportamiento especial para el caso de argumentos de nombre de archivo cero, aún fallarían con un mensaje de error o uso cuando no se pasa ninguno.
  • Incluso los casos que intuitivamente sienten que deberían funcionar a menudo no lo harían. printf 'Got file: "%s"\n' *.txtimprimiría en Got file: ""lugar de nada.
  • La falla accidental en citar ocurrencias de *, ?y [que no están destinadas a ser expandidas por el shell produciría con mayor frecuencia resultados obviamente erróneos, pero en formas que podrían ser difíciles de resolver. Por ejemplo, si no comenzó ningún nombre de archivo en el directorio actual gedit, entonces apt list gedit*(donde apt list 'gedit*'se pretendía) sería justo apt listy enumeraría todos los paquetes disponibles.

Por lo tanto, es bueno que no obtenga este comportamiento sin solicitarlo. Probablemente la situación práctica más común que en realidad se simplifica nullglobes for f in *.txt. Ver también esta pregunta (a la que se vincula la respuesta de Sergiy Kolodyazhnyy ).

La pregunta más difícil de responder es por qué, failglobdonde es un error de expansión tener un globo que no coincide con ningún archivo, no es el valor predeterminado en bash. Creo que la respuesta de Sergiy Kolodyazhnyy captura la razón de esto, incluso sin abordarla directamente. Retener globos sin expandir sin producir un error de expansión es (quizás desafortunadamente) el comportamiento estandarizado, y también es un comportamiento tradicional, y por lo tanto esperado. Aunque bash no intenta ser totalmente compatible con POSIX a menos que se invoque con el nombre sho se le pase la --posixopción, muchas de sus opciones de diseño incluso cuando no está en modo POSIX siguen a POSIX directamente. Tuvieron que elegir algún comportamiento, y hay desventajas asociadas con ir en contra de las expectativas de los usuarios.


Creo que este es el aspecto menos influyente históricamente del asunto, así que lo he guardado para el final ... pero vale la pena mencionar que hay algo un poco conceptualmente extraño en el nullglobcomportamiento.

nullglobparece elegante al principio porque, sintácticamente , trata el caso de cero archivos coincidentes no de manera diferente al caso de uno, dos o cualquier otro número. Los comandos que ejecutamos, para los cuales los globos se expanden en argumentos, no tienden a tratarlos de la misma manera, como se detalla anteriormente. Pero sintácticamente, esto al menos se siente bien, lo cual creo que es la motivación para su pregunta.

Y, sin embargo, hay otra inconsistencia más sutil que nullglobno aborda, que en realidad amplifica. El caso de los caracteres de globulo cero ("comodines") se trata de manera profundamente diferente al de uno, dos o cualquier otro número. Por ejemplo, con shopt -s nullglob, si ab?d?fno coincide con ningún archivo, se elimina; si ab?dno coincide con ningún archivo, se elimina; pero si abno coincide con ningún archivo (es decir, si no hay ningún archivo cuyo nombre sea exactamente ab) aún no se elimina. Por supuesto, sería un desastre si se eliminara, ya que podría no estar destinado a referirse a un archivo existente en el directorio actual; Puede que ni siquiera se refiera a un archivo. Pero esto todavía elimina cualquier esperanza de consistencia total.

Los tres comportamientos que proporciona bash: el valor predeterminado de tratar los globos que no coinciden con ningún archivo como si no fueran globos y pasarlos sin expandir, el comportamiento que esperaba de tratarlos (si perdona este extraño giro de la frase) como significar todos los cero de los archivos que coinciden ( nullglob), y el comportamiento seguro de considerarlos errores ( failglob): todos representan enfoques diferentes de la ambigüedad inherente en el shell de no poder saber si alguna palabra en particular está destinada a ser un nombre del archivo. El shell realiza sus expansiones sin saber cómo los comandos particulares que llama con él tratarán sus argumentos.

Este es uno de los muchos casos de separación de preocupaciones . En sistemas cuyo diseño sigue la filosofía de Unix, cada parte está destinada a hacer una cosa y hacerlo bien . El shell procesa el texto en comandos y argumentos e invoca esos comandos, la mayoría de los cuales son externos al propio shell. Esto tiende a ser un montón más bonito y más versátil que los sistemas en que los comandos externos son ellos mismos responsables de realizar estas transformaciones (al igual que con los procesadores tradicionales de comando en DOS y Windows). Pero tiene sus inconvenientes ocasionales.

Eliah Kagan
fuente
Aparentemente, bash-4.3.39 (2) no tiene failglob. Por lo tanto, no puede ser el valor predeterminado porque no siempre ha sido compatible.
Ruslan
6

La razón principal es porque este es el comportamiento estándar especificado por POSIX , el estándar que cubre el lenguaje de comandos de shell y, entre otras cosas, la coincidencia de patrones (shells como bash, dashshell, el valor predeterminado de Ubuntu /bin/shy kshsigue este estándar). De la sección 2.13.3 Patrones utilizados para la expansión del nombre de archivo :

Si el patrón no coincide con ningún nombre de archivo o ruta existente, la cadena del patrón no se modificará.

Por supuesto, esto tiene un efecto secundario: coincidencia del nombre de archivo que puede ser literalmente *.txt. La nullglobopción bashy zshpuede ayudar: si esa opción está habilitada a través de shopt -s nullglob(y no está habilitada de forma predeterminada, lo que se aplica a esta pregunta), globstar se expandirá a una cadena vacía cuando no se encuentren nombres de archivo coincidentes. ksh93tiene su propio mecanismo avanzado de coincidencia de patrones que logra el mismo efecto~(N)*.txt

Consulte también ¿Por qué nullglob no es predeterminado?

Sergiy Kolodyazhnyy
fuente