先に結論
- bashシェルスクリプト中の文字列は、半角スペースを区切りとして配列ライクに扱える。
- 文字列中の任意の区切り文字を、trなどで半角スペースに置換すれば配列ライクに扱える。
- ただし、通常配列で使えるような機能には使えるものと使えないものがある。
- 文字列を分割した配列でもできること(※elはelementのel)
-
for el in $ary;do; ... done;
で各要素を参照 -
ary = ($ary[@] $newEl)
やary+=(newEl)
で新しい要素を追加
-
- できないこと
-
echo ${ary[0]}
のようにインデックスを指定しての参照 -
${#ary[@]}
での要素数の参照
-
- その他、通常の配列との動作の違い
- 関数の引数に渡した場合の挙動(末尾の特記事項参照)
- 文字列を分割した配列でもできること(※elはelementのel)
- そもそもbashでは通常の配列も使いにくいよ!
(参考:プログラマーの君! 騙されるな! シェルスクリプトはそう書いちゃ駄目だ!! という話)
ふつうの配列の場合
スクリプト
# 宣言・定義
declare -a normalAry
normalAry=(a b c d)
# 配列の全要素を表示
echo ${normalAry[@]}
# 特定の要素を参照
echo ${normalAry[0]}
echo ${normalAry[2]}
# 配列の要素数を参照
echo ${#normalAry[@]}
実行結果
a b c d
a
c
4
計画通り。
文字列を分割した配列の場合
スクリプト
# 宣言・定義
str="what:a:wonderful:world"
declare -a splitedAry
splitedAry=$(echo `echo "$str" | tr ':' ' '`)
# 配列の全要素を表示
echo ${splitedAry[@]}
# 特定の要素を参照
echo ${splitedAry[0]}
echo ${splitedAry[2]}
# 配列の要素数を参照
echo ${#splitedAry[@]}
実行結果
what a wonderful world
what a wonderful world
1
要素数は1、インデックス0に全文が入っており、インデックス2の要素はない(NULL)、という結果。
"what a wonderful world"というひとつの要素が入っているだけ。
それって配列なの?
上記を見ると配列とは言いがたいのだが、不思議なことにfor文で回すことはできる。
スクリプト
for el in $splitedAry; do
echo $el
done
実行結果
what
a
wonderful
world
あれ、じゃあ元から半角スペース区切りの文字列は…?
スクリプト
str2="what a wonderful world"
for el in $str2; do
echo $el
done
実行結果
what
a
wonderful
world
同じである。
どういうこと?
IFSが半角スペースなので、配列とは違うんだけど「複数要素の集まり」とは見做される模様。勿論""で囲めばまともに文字列として扱われる。
特記事項:関数の引数に渡した場合
使う関数
sub(){
echo $# # 渡された引数の数
for el in $@; do # 全引数を一行ずつに区切って表示
echo $el
done
}
普通の配列の場合の実行結果
ary=(a b c d)
を渡した場合。ご存知の人は多いと思いますが、配列は普通に渡しただけでは関数内で配列として振舞ってくれません。
1
a
引数の数としては1で(感覚的に)合っているのですが、配列内の全要素を取ることはできません。
※軽く調べたところ、引数に配列を使う方法としてはこんなのがあるようです(未検証):
bashで引数に配列を設定する方法 - 小粋空間
文字列を分割した配列の場合の実行結果(スペース区切り文字列も同じ)
上記と同じ"what a wonderful world"の結果です。
4
what
a
wonderful
world
配列が展開されて、4つの引数が渡されたことになっています。
なお、渡す際にダブルクォートでsub "$splitedAry"
とかsub "$str2"
とすると、いずれも引数の数($#
)は1になります。
しかしながら上記関数と同様にfor el in $@
で回すと全要素取れます。不思議!
1
what
a
wonderful
world
関数に配列を渡そうとするのは、可読性の観点からも止めた方がいいというのが私の見解です。サブルーチンらしく、呼び出し元でforを回して、forの中から呼びましょう。