Tengo el siguiente script que se supone que resuelve el problema del título, pero obviamente no funciona para asignar los valores a las claves. ¿El motivo es un error accidental o hay un error sustancial en el guión?
Los nombres de las carpetas son nombres de archivos de gemas comogem-file-foo-1.2.3
Se supone que la clave gem-file-foo
en este ejemplo y los valores son el número de versión 1.2.3
o una cadena de múltiples números de versión si hay múltiples versiones de la misma gema.
No genera ninguna clave con echo "${!my_gems[@]}"
...¿por qué no?
#!/bin/bash
directory=$GEM_HOME/gems
declare -A my_gems
get_gemset_versions () {
last_key=""
values=""
FIND_RESULTS=$(find $directory -maxdepth 1 -type d -regextype posix-extended -regex "^${directory}\/[a-zA-Z0-9]+([-_]?[a-zA-Z0-9]+)*-[0-9]{1,3}(.[0-9]{1,3}){,3}\$")
printf "%s\n" $FIND_RESULTS | sort |
while read -r line; do
line=${line##*/}
KEY="${line%-*}"
VALUE="${line##*-}"
if [[ $last_key -eq "" ]]; then
last_key=$KEY
fi
if [[ $last_key -eq $KEY ]]; then
values="$values ${VALUE}"
else
values="${VALUE}"
last_key=$KEY
fi
my_gems[$KEY]=$values
done
echo "${!my_gems[@]}"
}
get_gemset_versions
Además, la lógica con $last_key
y $key
para resumir paquetes de gemas iguales parece ser defectuosa. Esto no es necesariamente parte de la pregunta, pero sería bueno que señalara si estoy aplicando alguna lógica defectuosa aquí.
Gracias
Respuesta1
Tienes:
printf "%s\n" $FIND_RESULTS | sort |
while read -r line; do
...
done
echo "${!my_gems[@]}"
donde, independientemente de la sangría, echo
está fuera de la tubería. Bash ejecuta todas las partes de una canalización en subcapas de forma predeterminada, por lo que las asignaciones dentro del while
bucle no son visibles una vez finalizada la canalización. Shellcheck.net también advierte sobre eso:
Line 32:
my_gems[$KEY]=$values
^-- SC2030: Modification of my_gems is local (to subshell caused by pipeline).
Lamentablemente, no ofrece soluciones alternativas.
En Bash, puede habilitar la lastpipe
opción o reemplazar la tubería con sustitución de proceso:
shopt -s lastpipe
echo test | while read line; do
out=$line
done
echo "out=$out"
o
while read line; do
out=$line
done < <(echo test)
echo "out=$out"
( lastpipe
probablemente no funcione si lo prueba en un shell interactivo, ya que está vinculado al control del trabajonosiendo habilitado.)
En cualquier caso, esto parece un poco extraño:
FIND_RESULTS=$(find ...)
printf "%s\n" $FIND_RESULTS
find
genera nombres de archivos separados por nuevas líneas, lo cual está bien siempre y cuando sepa que ningún nombre de archivo contiene ninguno. Pero aquí, el recorrido de ida y vuelta a través de la variable y la división de palabras de la expansión sin comillas también divide cualquier nombre de archivo con espacios.
Podrías simplemente correr find ... | while ...
directamente. O while ...; done < <(find...)
.
Además, tenga en cuenta que casi siempre desea utilizar while IFS= read -r line; do
, para evitar read
romper los espacios en blanco iniciales y finales. Bueno, espero que tus nombres de archivos tampoco los contengan, pero en cualquier caso.
No puedo encontrar una buena pregunta de referencia en este momento, pero es específica para IFS
contener espacios en blanco. Otros separadores iniciales y finales no se eliminan con read
un solo campo. Por ejemplo, IFS=": " read -r foo <<< "::foobar "
se va foo
con el literal ::foobar
. Los dos puntos se mantienen, pero los espacios finales desaparecen.