Bash array con espacios en elementos

Estoy tratando de construir una matriz en bash de los nombres de archivo de mi cámara:

FILES=(2011-09-04 21.43.02.jpg 2011-09-05 10.23.14.jpg 2011-09-09 12.31.16.jpg 2011-09-11 08.43.12.jpg) 

Como puede ver, hay un espacio en el medio de cada nombre de archivo.

Intenté envolver cada nombre entre comillas y escapar del espacio con una barra diagonal inversa, ninguna de las cuales funciona.

Cuando bash acceder a los elementos de la matriz, continúa tratando el espacio como elementdelimiter.

¿Cómo puedo capturar adecuadamente los nombres de archivo con un espacio dentro del nombre?

Creo que el problema podría ser en parte con la forma en que está accediendo a los elementos. Si hago un for elem in $FILES simple for elem in $FILES , experimento el mismo problema que tú. Sin embargo, si accedo a la matriz a través de sus índices, así, funciona si agrego los elementos numéricamente o con escapes:

 for ((i = 0; i < ${#FILES[@]}; i++)) do echo "${FILES[$i]}" done 

Cualquiera de estas declaraciones de $FILES debería funcionar:

 FILES=(2011-09-04\ 21.43.02.jpg 2011-09-05\ 10.23.14.jpg 2011-09-09\ 12.31.16.jpg 2011-09-11\ 08.43.12.jpg) 

o

 FILES=("2011-09-04 21.43.02.jpg" "2011-09-05 10.23.14.jpg" "2011-09-09 12.31.16.jpg" "2011-09-11 08.43.12.jpg") 

o

 FILES[0]="2011-09-04 21.43.02.jpg" FILES[1]="2011-09-05 10.23.14.jpg" FILES[2]="2011-09-09 12.31.16.jpg" FILES[3]="2011-09-11 08.43.12.jpg" 

Debe haber algún problema con la forma de acceder a los elementos de la matriz. Así es como se hace:

 for elem in "${files[@]}" ... 

De la página de manual de bash :

Se puede hacer referencia a cualquier elemento de una matriz usando $ {name [subscript]}. … Si el subíndice es @ o *, la palabra se expande a todos los miembros del nombre. Estos subíndices difieren solo cuando la palabra aparece entre comillas dobles. Si la palabra tiene comillas dobles, $ {name [*]} se expande a una sola palabra con el valor de cada miembro de la matriz separado por el primer carácter de la variable especial IFS, y $ {name [@]} expande cada elemento de nombre a una palabra separada .

Por supuesto, también debe usar comillas dobles cuando acceda a un único miembro

 cp "${files[0]}" /tmp 

Necesita usar IFS para detener el espacio como delimitador de elementos.

 FILES=("2011-09-04 21.43.02.jpg" "2011-09-05 10.23.14.jpg" "2011-09-09 12.31.16.jpg" "2011-09-11 08.43.12.jpg") IFS="" for jpg in ${FILES[*]} do echo "${jpg}" done 

Si desea separar en base a. entonces simplemente haz IFS = “.” Espero que te ayude 🙂

Estoy de acuerdo con otros en que es probable que la forma de acceder a los elementos sea el problema. Citar los nombres de los archivos en la asignación de la matriz es correcto:

 FILES=( "2011-09-04 21.43.02.jpg" "2011-09-05 10.23.14.jpg" "2011-09-09 12.31.16.jpg" "2011-09-11 08.43.12.jpg" ) for f in "${FILES[@]}" do echo "$f" done 

El uso de comillas dobles alrededor de cualquier matriz del formulario "${FILES[@]}" divide la matriz en una palabra por elemento de conjunto. No hace ninguna división de palabras más allá de eso.

El uso de "${FILES[*]}" también tiene un significado especial, pero se une a los elementos de la matriz con el primer carácter de $ IFS, lo que da como resultado una palabra, que probablemente no sea la que desea.

El uso de ${array[@]} o ${array[*]} somete el resultado de esa expansión a una división de palabras adicional, por lo que terminará con palabras divididas en espacios (y cualquier otra cosa en $IFS ) en su lugar de una palabra por elemento de conjunto.

Usar un estilo C para bucle también es bueno y evita preocuparse por la división de palabras si no lo tiene claro:

 for (( i = 0; i < ${#FILES[@]}; i++ )) do echo "${FILES[$i]}" done 

Escapar funciona

 #!/bin/bash FILES=(2011-09-04\ 21.43.02.jpg 2011-09-05\ 10.23.14.jpg 2011-09-09\ 12.31.16.jpg 2011-09-11\ 08.43.12.jpg) echo ${FILES[0]} echo ${FILES[1]} echo ${FILES[2]} echo ${FILES[3]} 

Salida:

 $ ./test.sh 2011-09-04 21.43.02.jpg 2011-09-05 10.23.14.jpg 2011-09-09 12.31.16.jpg 2011-09-11 08.43.12.jpg 

Citar las cuerdas también produce el mismo resultado.

No es exactamente una respuesta al problema de citar / escapar de la pregunta original, pero probablemente algo que en realidad hubiera sido más útil para la operación:

 unset FILES for f in 2011-*.jpg; do FILES+=("$f"); done echo "${FILES[@]}" 

Donde, por supuesto, la expresión tendría que ser adoptada para el requisito específico (por ejemplo, *.jpg para todos o 2001-09-11*.jpg para solo las imágenes de un día determinado).

Otra solución es usar un bucle “while” en lugar de un bucle “for”:

 index=0 while [ ${index} -lt ${#Array[@]} ] do echo ${Array[${index}]} index=$(( $index + 1 )) done