タイトルの問題を解決するはずの次のスクリプトがありますが、明らかにキーに値を割り当てることができません。原因は偶発的なエラーですか、それともスクリプトに重大な間違いがあるのでしょうか?
フォルダ名はgemファイルの名前です。gem-file-foo-1.2.3
この例では、キーはgem-file-foo
、値はバージョン番号、1.2.3
または同じ gem に複数のバージョンがある場合は複数のバージョン番号の文字列になります。
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
また、等しい gem パッケージをまとめるためのおよびのロジック$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 つの 'while read' ループではローカルであるのに、他の類似したループではローカルではないのはなぜですか?
いずれにせよ、これは少し奇妙に思えます:
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