У меня есть следующий скрипт, который должен решить проблему из названия, но, очевидно, он не работает, чтобы назначить значения ключам. Является ли причиной случайная ошибка или в скрипте есть существенная ошибка?
Имена папок — это имена 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
вероятно, не сработает, если вы попробуете это в интерактивной оболочке, так как это связано с управлением заданияминет(будучи включенным.)
В любом случае, это кажется немного странным:
FIND_RESULTS=$(find ...)
printf "%s\n" $FIND_RESULTS
find
выводит имена файлов, разделенные символами новой строки, что нормально, пока вы знаете, что ни одно имя файла не содержит ничего. Но здесь круговой обход через переменную и разделение слов из некавычек также разделяет любые имена файлов с пробелами.
Вы можете просто бежать find ... | while ...
напрямую. Или while ...; done < <(find...)
.
Также, обратите внимание, что вы почти всегда хотите использовать while IFS= read -r line; do
, чтобы предотвратить read
разрыв начальных и конечных пробелов. Ну, я надеюсь, что ваши имена файлов их тоже не содержат, но в любом случае.
Я не могу найти хороший справочный вопрос прямо сейчас, но это относится к IFS
содержанию пробелов. Другие начальные и конечные разделители не удаляются с помощью read
to только одного поля. Например, IFS=": " read -r foo <<< "::foobar "
листья foo
с литералом ::foobar
. Двоеточия сохраняются, но конечные пробелы исчезают.