Как извлечь папки, разделить имена файлов на ключи и значения и сохранить их в ассоциативном массиве?

Как извлечь папки, разделить имена файлов на ключи и значения и сохранить их в ассоциативном массиве?

У меня есть следующий скрипт, который должен решить проблему из названия, но, очевидно, он не работает, чтобы назначить значения ключам. Является ли причиной случайная ошибка или в скрипте есть существенная ошибка?

Имена папок — это имена gem-файлов, напримерgem-file-foo-1.2.3

В этом примере предполагается, что ключ — это gem-file-fooномер версии 1.2.3или строка из нескольких номеров версий, если существует несколько версий одного и того же gem-файла.

Он не выводит никаких ключей с echo "${!my_gems[@]}"...почему нет?

#!/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

Также логика с $last_keyи $keyдля суммирования равных пакетов драгоценных камней кажется ошибочной. Это не обязательно часть вопроса, но было бы неплохо, если бы вы указали, применяю ли я здесь какую-то ошибочную логику.

Спасибо

решение1

У вас есть:

printf "%s\n" $FIND_RESULTS | sort | 
  while read -r line; do
    ...
    done
 
    echo "${!my_gems[@]}"

где, независимо от отступа, находится echoвне конвейера. Bash по умолчанию запускает все части конвейера в подоболочках, поэтому назначения внутри цикла whileне видны после завершения конвейера. Shellcheck.net также предупреждает об этом:

Line 32:
        my_gems[$KEY]=$values
        ^-- SC2030: Modification of my_gems is local (to subshell caused by pipeline).

К сожалению, обходных путей не предусмотрено.

В Bash вы можете либо включить эту lastpipeопцию, либо заменить канал на подстановку процесса:

shopt -s lastpipe
echo test | while read line; do
    out=$line
  done
echo "out=$out"

или

while read line; do
  out=$line
done < <(echo test)
echo "out=$out"

( lastpipeвероятно, не сработает, если вы попробуете это в интерактивной оболочке, так как это связано с управлением заданияминет(будучи включенным.)

Видеть:Почему моя переменная является локальной в одном цикле «while read», но не в другом, на первый взгляд похожем цикле?


В любом случае, это кажется немного странным:

FIND_RESULTS=$(find ...)

 printf "%s\n" $FIND_RESULTS 

findвыводит имена файлов, разделенные символами новой строки, что нормально, пока вы знаете, что ни одно имя файла не содержит ничего. Но здесь круговой обход через переменную и разделение слов из некавычек также разделяет любые имена файлов с пробелами.

Вы можете просто бежать find ... | while ...напрямую. Или while ...; done < <(find...).

Также, обратите внимание, что вы почти всегда хотите использовать while IFS= read -r line; do, чтобы предотвратить readразрыв начальных и конечных пробелов. Ну, я надеюсь, что ваши имена файлов их тоже не содержат, но в любом случае.

Я не могу найти хороший справочный вопрос прямо сейчас, но это относится к IFSсодержанию пробелов. Другие начальные и конечные разделители не удаляются с помощью readto только одного поля. Например, IFS=": " read -r foo <<< "::foobar "листья fooс литералом ::foobar. Двоеточия сохраняются, но конечные пробелы исчезают.

Связанный контент