タイトルの問題を解決する必要がある次のスクリプトがありますが、キーに値を割り当てることができないようです。原因が予期しないエラーでしたか?それともスクリプトの主なバグでしたか?
フォルダ名は 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
。コロンは保持されますが、末尾のスペースは消えます。