bashでtsvファイルを連想配列に入れる時にハマったこと
概要
bashでtvsファイルを読み込んで連想配列に入れたい処理があり、うまくできず2時間程度ハマりました。最終的にはうまくできましたが、結構典型的なことだと思いますので、まとめてみました。
tsvファイル
例示するため、tsvファイルは以下のものを使います。
JPY<tab>日本円 USD<tab>ドル CNY<tab>中国人民元 EUR<tab>ユーロ
このtsvファイルを詠込んで、次のような連想配列に入れる想定
currency( ["JPY"]="日本円" ["USD"]="ドル" ["CNY"]="中国人民元" ["EUR"]="ユーロ" )
shell script
#!/usr/bin/env bash TSV_FILE =./currency.tsv declare -A currency cat $TSV_FILE | while read code name; do echo [$code] [$name] currency+=([$code]=$name) done echo ${currency[@]}
実行結果
$ bash tsv2dict.sh [JPY] [日本円] [USD] [ドル] [CNY] [中国人民元] [EUR] [ユーロ]
あれれ、whileループ内でちゃんとtsvファイルから値を取得できたことを検証できているのに、ループを抜け後に、値を入れたはずのcurrency連想配列には何も入っていない、なぞで仕方ありません。
原因
whileが悪いではなく、cat $TSV_FILEの結果をパイプで while ループに渡してたのがいけませんでした。
bashでは、パイプでデータを while ループに渡した後の処理は、子プロセス(fork)として動くため、親プロセスの変数は受け取れて使えるが、子プロセス内に変更した値が、親プロセス内には反映されないことになるため、currency連想配列は空のママになっている訳です。
参考:シェルスクリプトのwhile文の中の変数を外で使う方法 - Qiita
解決方法
パイプで標準出力を渡すのではなく、ヒアドキュメントでwhileループにデータを渡せば、whileループ内の処理も親プロセル内になるため、currency変数の値は後から引き継がれる。
while read code name; do echo [$code] [$name] currency+=([$code]=$name) done << EOF $(cat $TSV_FILE) EOF echo ${currency[@]}
実行結果
$ bash tsv2dict.sh [JPY] [日本円] [USD] [ドル] [CNY] [中国人民元] [EUR] [ユーロ] ドル 日本円 中国人民元 ユーロ
連想配列から正しく出力できました。
参考:シェルスクリプトのwhile readループが悪いのか?回避策は? - Qiita