フォルダからファイル名を抽出し、キーと値を分割して連想配列に保存する方法は?

フォルダからファイル名を抽出し、キーと値を分割して連想配列に保存する方法は?

タイトルの問題を解決する必要がある次のスクリプトがありますが、キーに値を割り当てることができないようです。原因が予期しないエラーでしたか?それともスクリプトの主なバグでしたか?

フォルダ名は 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はパイプラインのすべての部分をサブシェルで実行するため、パイプラインの終了後にループ内の割り当ては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インタラクティブシェルで試してみると、操作制御に関連して機能しない可能性があります。いいえ活性化。 )

望むより:私の変数が1つの「読み込み中」ループではローカルですが、一見似ている他のループではローカルではないのはなぜですか?


とにかく、これはちょっと変に見えます。

FIND_RESULTS=$(find ...)

 printf "%s\n" $FIND_RESULTS 

find改行で区切られたファイル名を出力します。ファイル名にファイル名が含まれていないことを知っていれば大丈夫です。ただし、変数のラウンドトリップと引用符で囲まれていない拡張子の単語分割は、すべてのファイル名をスペースに分割します。

ただ実行してくださいfind ... | while ...。またはwhile ...; done < <(find...)

また、先行スペースと末尾のスペースが壊れるのをwhile IFS= read -r line; do防ぐためにほとんど常に使用したいことに注意してください。readまあ、あなたのファイル名にもそのような内容が含まれていないことを願っています。

今は良い参照質問を見つけることができませんが、これはIFSスペースを含むだけです。他の先行および末尾の区切り文字は、1つのreadフィールドのみを使用して削除されません。たとえば、IFS=": " read -r foo <<< "::foobar "休暇はfoo文字通りを意味します::foobar。コロンは保持されますが、末尾のスペースは消えます。

おすすめ記事