Tengo el siguiente script bash simplificado
#!/bin/bash
files=("$@")
if [ "X$files" = "X" ]; then
files=$HOME/print/*.pdf;
fi
for file in "${files[@]}"; do
ls "$file";
done
Si paso argumentos (nombres de archivo) como parámetros, este script imprimirá los nombres de archivo adecuados. Por otro lado, si no paso argumentos, imprimirá
/home/user/print/*.pdf: No such file or directory
¿Por qué los nombres de archivo no se expanden en este caso y cómo lo soluciono? Tenga en cuenta que uso las construcciones files=("$@")
y "${files[@]}"
porque leí que es preferible a los "archivos = $ *" habituales.
files=$*
siempre usual ? Eso está completamente mal .Respuestas:
Estás asignando
files
como una variable escalar en lugar de una variable de matriz .En
Estás asignando una cadena similar
/home/highsciguy/print/*.pdf
a la$files
variable escalar (también conocida como cadena).Utilizar:
o
en lugar. El shell expandirá ese patrón global en una lista de rutas de archivo y asignará cada una de ellas a los elementos de la
$files
matriz .La expansión del globo se realiza en el momento de la asignación.
No tiene que usar funciones sh no estándar, y puede usar las de su sistema en
sh
lugar debash
aquí escribiéndolas:set
es asignar la"$@"
matriz de parámetros posicionales.Otro enfoque podría haber sido almacenar el patrón globbing en una variable escalar:
Y haga que el shell expanda el glob en el momento en
$files
que se expande la variable.Aquí, debido a
$files
que no se cita (lo que normalmente no se debe hacer), su expansión está sujeta a la división de palabras (que hemos desactivado aquí) y la generación de nombres de archivo / globbing.Por lo tanto
*.pdf
, se expandirá a la lista de archivos coincidentes. Sin embargo, si$HOME
contuviera caracteres comodín, también podrían expandirse, por lo que todavía es preferible usar una variable de matriz.fuente
Es posible que haya visto cosas como
files=$*
yfiles=~/print/*.pdf
en conchas antiguas sin matrices, y luegols $files
.Una sustitución de variable que no está entre comillas dobles interpreta el valor de la variable como una lista separada por espacios en blanco de patrones comodín de shell que se reemplazan por nombres de archivo coincidentes, si los hay. Por ejemplo, después
files=~/print/*.pdf
, sels $files
expande a algo así comols
con los argumentos/home/highsciguy/print/bar.pdf
,/home/highsciguy/print/foo.pdf
etc. En el casofiles=$*
, esta asignación concatena los argumentos pasados al script con espacios en el medio y losls $files
divide de nuevo.Todo esto se descompone si tiene nombres de archivo que contienen espacios en blanco o caracteres globales, por lo que no debe hacer las cosas de esta manera. Utilice matrices en su lugar.
Tenga en cuenta que
var=(…)
."$files"
está vacío cuandofiles
es una matriz cuyo elemento del índice 0 no está establecido o una cadena vacía. También[ "X$foo" = "X" ]
es una forma obsoleta de probar si$foo
está vacía: todos los shells modernos se implementan[ -n "$foo" ]
correctamente. En bash, puedes usar[[ -n $foo ]]
.En los shells que no admiten arrays, de hecho hay un array: los parámetros posicionales para el shell o la función actual. Aquí, realmente no necesita la
files
matriz, de hecho, sería más fácil usar los parámetros posicionales.fuente