我有以下脚本,应该可以解决标题中的问题,但显然它无法将值分配给键。原因是意外错误还是脚本中存在重大错误?
文件夹名称是 gem 文件的名称,例如gem-file-foo-1.2.3
在此示例中,键应该是版本号,如果同一个 gem 有多个版本,gem-file-foo
则值应该是版本号或多个版本号的字符串。1.2.3
它不会输出任何键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 在子 shell 中运行管道的所有部分,因此循环内的分配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
如果您在交互式 shell 中尝试它可能不起作用,因为它与作业控制相关不是正在启用。)
看:为什么我的变量在一个“while read”循环中是本地变量,但在另一个看似相似的循环中却不是?
无论如何,这看起来有点奇怪:
FIND_RESULTS=$(find ...)
printf "%s\n" $FIND_RESULTS
find
输出由换行符分隔的文件名,只要您知道文件名不包含任何文件名就可以。但在这里,变量的往返以及未加引号的扩展中的单词分割也会用空格分割任何文件名。
直接跑就可以了find ... | while ...
。或者while ...; done < <(find...)
。
另请注意,您几乎总是希望使用while IFS= read -r line; do
, 来防止read
破坏前导和尾随空格。好吧,我希望你的文件名也不包含这些内容,但无论如何。
我现在找不到好的参考问题,但这是特定于IFS
包含空格的。其他前导和尾随分隔符不会read
仅使用一个字段来删除。例如,IFS=": " read -r foo <<< "::foobar "
离开的foo
字面意思是::foobar
。冒号被保留,但尾随空格消失。