¿Por qué son necesarias las comillas para el argumento de los archivos cuando se llama a este script Bash?

13

Soy bastante nuevo en las secuencias de comandos Bash. Tengo un "script de prueba", que utilicé como base para un script más avanzado / útil:

#!/bin/bash
files=$1
for a in $files
do
    echo "$a"
done

Cuando llamo a esto sin comillas, solo recoge un archivo en un directorio:

testscript *.txt

Pero cuando lo llamo con comillas, funciona correctamente y selecciona todos los archivos de texto:

testscript '*.txt'

¿Que esta pasando aqui?

gornvix
fuente
Para ser muy, muy claro, la forma correcta de solucionar esto es ejecutar for a in "$@"; do(o for a; do) en su secuencia de comandos, dejando de este modo la capa exterior, no dejar de lado las comillas.
Charles Duffy el
Vale la pena echarle un vistazo. guide.bash.academy
vascowhite

Respuestas:

29

Cuando llamas a un programa

testscript *.txt

entonces su shell hace la expansión y calcula todos los valores. Por lo tanto, podría llamar efectivamente a su programa como

testscript file1.txt file2.txt file3.txt file4.txt

Ahora su programa solo mira $1y solo funciona file1.txt.

Al citar en la línea de comando, está pasando la cadena literal *.txtal script, y eso es lo que se almacena $1. Su forciclo luego lo expande.

Normalmente lo usarías "$@"y no $1en scripts como este.

Este es un "problema" para las personas que provienen de las secuencias de comandos CMD, donde el shell de comandos no hace glob (como se sabe) y siempre pasa la cadena literal.

Stephen Harris
fuente
66
Solo para aclarar (para personas que no sean el autor de la respuesta anterior), usar "$@"(en lugar de $@o $1 $2 $3) hará que se cite cada nombre de archivo, "file1.txt" "file2.txt"etc. Para file1.txtesto no tiene sentido, pero si lo tiene my file.txt, la cita es crítica para evitar el shell análisis para convertirlo en dos nombres de archivo, uno con nombre myy otro con nombre file.txt. Siempre cite la entrada del usuario y la expansión global para que no se sienta muy infeliz algún día.
Seth Robertson
2
Y esto no es solo teórico: Mac OS X una vez se envió con un script de actualización que no citaba argumentos adecuadamente y terminó borrando los discos duros de las personas en algunas circunstancias.
esponjoso
2
@ Fluffy, ¿tienes un enlace sobre eso?
Comodín el
@Wildcard Lamentablemente, no puedo encontrar ningún artículo al respecto, pero fue una gran noticia en el mundo de la tecnología cuando sucedió. Quiero decir que fue en 2003/2004 o por ahí, cuando Apple todavía se estaba acostumbrando a ser un distribuidor de UNIX.
esponjoso
1
@wildcard ¡Ah, lo encontré! xlr8yourmac.com/OSX/itunes2_erased_drives.html : en realidad fue el guión de actualización de iTunes el culpable.
esponjoso
7

Sin comillas, el shell se expande *.txtantes de invocar el script, por $1lo que solo se expande el primer archivo. Todos los txtarchivos son argumentos de su secuencia de comandos en ese punto (suponiendo que no haya demasiados).

Con comillas, esa cadena se pasa sin expandirse al script, lo que luego le permite forhacer la expansión, como espera.

Eric Renouf
fuente