如何提取文件夹、拆分键和值中的文件名并将其保存在关联数组中?

如何提取文件夹、拆分键和值中的文件名并将其保存在关联数组中?

我有以下脚本,应该可以解决标题中的问题,但显然它无法将值分配给键。原因是意外错误还是脚本中存在重大错误?

文件夹名称是 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。冒号被保留,但尾随空格消失。

相关内容