¿Cómo extraer carpetas, dividir nombres de archivos en claves y valores y guardarlos en una matriz asociativa?

¿Cómo extraer carpetas, dividir nombres de archivos en claves y valores y guardarlos en una matriz asociativa?

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-fooen este ejemplo y los valores son el número de versión 1.2.3o 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_keyy $keypara 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, echoestá 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 whilebucle 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 lastpipeopció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"

( lastpipeprobablemente no funcione si lo prueba en un shell interactivo, ya que está vinculado al control del trabajonosiendo habilitado.)

Ver:¿Por qué mi variable es local en un bucle 'mientras se lee', pero no en otro bucle aparentemente similar?


En cualquier caso, esto parece un poco extraño:

FIND_RESULTS=$(find ...)

 printf "%s\n" $FIND_RESULTS 

findgenera 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 readromper 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 IFScontener espacios en blanco. Otros separadores iniciales y finales no se eliminan con readun solo campo. Por ejemplo, IFS=": " read -r foo <<< "::foobar "se va foocon el literal ::foobar. Los dos puntos se mantienen, pero los espacios finales desaparecen.

información relacionada