Eu tenho o seguinte script que deveria resolver o problema do título, mas obviamente não está funcionando para atribuir os valores às chaves. O motivo é um erro acidental ou há um erro substancial no script?
Os nomes das pastas são nomes de arquivos gem comogem-file-foo-1.2.3
A chave deve estar gem-file-foo
neste exemplo e o(s) valor(es) é o número da versão 1.2.3
ou uma sequência de vários números de versão se houver várias versões da mesma gema.
Ele não gera nenhuma chave com echo "${!my_gems[@]}"
...por que não?
#!/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
Além disso, a lógica com $last_key
e $key
para verão pacotes de gemas iguais parece estar com defeito. Isso não faz necessariamente parte da questão, mas seria bom se você apontasse se estou aplicando alguma lógica errada aqui.
Obrigado
Responder1
Você tem:
printf "%s\n" $FIND_RESULTS | sort |
while read -r line; do
...
done
echo "${!my_gems[@]}"
onde, independentemente do recuo, o echo
está fora do pipeline. O Bash executa todas as partes de um pipeline em subshells por padrão, portanto, as atribuições dentro do while
loop não ficam visíveis após o término do pipeline. Shellcheck.net também alerta sobre isso:
Line 32:
my_gems[$KEY]=$values
^-- SC2030: Modification of my_gems is local (to subshell caused by pipeline).
Infelizmente, isso não oferece soluções alternativas.
No Bash, você pode ativar a lastpipe
opção ou substituir o pipe pela substituição do processo:
shopt -s lastpipe
echo test | while read line; do
out=$line
done
echo "out=$out"
ou
while read line; do
out=$line
done < <(echo test)
echo "out=$out"
( lastpipe
provavelmente não funcionará se você tentar em um shell interativo, pois está vinculado ao controle de tarefasnãosendo habilitado.)
De qualquer forma, isso parece um pouco estranho:
FIND_RESULTS=$(find ...)
printf "%s\n" $FIND_RESULTS
find
gera nomes de arquivos separados por novas linhas, o que é bom, desde que você saiba que nenhum nome de arquivo contém nenhum. Mas aqui, o percurso de ida e volta pela variável e a divisão de palavras da expansão sem aspas também divide quaisquer nomes de arquivos com espaços.
Você poderia simplesmente executar find ... | while ...
diretamente. Ou while ...; done < <(find...)
.
Além disso, observe que você quase sempre deseja usar while IFS= read -r line; do
, para evitar read
a quebra de espaços em branco iniciais e finais. Bem, espero que seus nomes de arquivos também não os contenham, mas de qualquer forma.
Não consigo encontrar uma boa pergunta de referência no momento, mas isso é específico para IFS
conter espaços em branco. Outros separadores iniciais e finais não são removidos com read
apenas um campo. Por exemplo, IFS=": " read -r foo <<< "::foobar "
sai foo
com o literal ::foobar
. Os dois pontos são mantidos, mas os espaços finais desaparecem.