配列 - Bash Advent Calendar - Day1

毎日続くかどうかわかりませんが、できる限り続けていきたいと思います。

さて、何から書いていいかわからないので、とりあえず最初は A から始まる単語はないかと探してみたところ、alias, array, associative array, argument, arithmetic evaluation と、とりあえず数日はいけそうな感じ。

というわけで今日は配列について。

まず注意点として、配列は sh にはない Bash 独自の機能です。なので #!/bin/sh で書き始めてあるスクリプト内で使うと、後々困ることがあるかもしれなので要注意。そもそも /bin/sh が Bash ってどうなのという議論は勝手にやりたい人だけがしてください。

変数の宣言、初期化

ちょっとかしこまった宣言

普段目にすることはあまりないかもしれませんが、C 言語出身者が好むスタイル(偏見100%)。
$ declare -a foo
これで変数 foo は配列変数になります。

変数には初期値を入れた明示的な初期化が必要と考える、gccマンセー、もしくはC++出身者であれば(これも偏見100%、以下同じ)
$ declare -a foo=()
で、同じ中身が空の配列変数が宣言されます。

ちなみに、想像で適当に書いてるので、実際にこんな空の配列を使ってるのを見たことは一度たりともありませんw

値の代入による初期化

普通の変数の場合、わざわざ宣言などせず、いきなり値を代入しますよね。配列変数においても同じです。ある書式にのっとって値を代入すれば勝手に配列変数になります。
$ foo=("bar" "baz")
中身が実際にどうなってるかを確認したい場合には、
$ declare -p foo
declare -a foo='([0]="bar" [1]="baz")'
というように declare -p を使いましょう。ちゃんと declare -a と配列変数になっているのが分かります。配列のインデックスもこれで確認できます。

さて、配列のインデックスが出てきたのでもう一つ。初期化時に、インデックスを指定することもできます。上の declare -p の出力から類推できるように、
$ foo=([1]="bar" [3]="baz")
$ declare -p foo
declare -a foo='([1]="bar" [3]="baz")'
とすることで、特定の位置に値を代入できます。またこの例から分かるように、インデックスは飛び飛びでも構いません。

最後にもう一つ、あまりおすすめしませんが
$ foo[1]="bar"
$ declare -p foo
declare -a foo='([1]="bar")'
という形の代入でも配列変数を作成できます。これは、本来は foo という配列変数が既に存在しているときにその値を書き換えるものです。なので、これを初期化に使ってしまうと、foo という変数がそれまでに初期化されてないのがバグなのか、それともこれが意図したものかが分かりづらくなってしまいます。

値の代入、追加

初期化時に値を代入する方法は既に述べました。ここでは既存の配列に値を追加、配列内の値を変更する方法について述べます。

まず、初期化の最後で述べた通り、
$ foo=("bar" "baz")
$ foo[1]="qux"
$ declare -p foo
declare -a foo='([0]="bar" [1]="qux")'
変数名に続けてインデックスを指定することで、特定のインデックスの値を変更、または挿入することができます。

"+=" を使って複数値をまとめて追加することもできます。
$ foo=("bar" "baz")
$ foo+=("qux" "quux")
$ declare -p foo
declare -a foo='([0]="bar" [1]="baz" [2]="qux" [3]="quux")'
"+=" は配列の末尾に与えられた配列を追加するので、その応用として
foo=("a" "b")
for ((i=0;i<10;i++)); do
  foo+=($i)
done
のように、値が1個だけの配列を使って、値を末尾に追加するのはよくあるパターンです。

一度たりとも使ったことがないし、他の人が使ってるのも見たことがないのですが、右辺の配列にインデックスが指定されていれば、その値が尊重されます。すなわち
$ foo=([0]="bar" [2]="qux")
$ foo+=([1]="baz" [3]="quux")
$ declare -p foo
declare -a foo='([0]="bar" [1]="baz" [2]="qux" [3]="quux")'
と配列のマージができるのですが、インデックスが重複した場合の挙動がお節介
$ foo=([0]="bar" [2]="qux")
$ foo+=([0]="baz" [1]="quux")
$ declare -p foo
declare -a foo='([0]="barbaz" [1]="quux" [2]="qux")'
だったり、そもそも
$ foo=([0]="bar" [2]="qux")
$ foo+=("baz" "quux")
$ declare -p foo
declare -a foo='([0]="bar" [2]="qux" [3]="baz" [4]="quux")'
と挙動が違う、すなわち ([0]="baz" [1]="quux") と ("baz" "quux") が等価ではない時点で糞仕様認定は避けられません。

予想外に長くなったので、明日に続く。

追記

この手のIT系 Advent Calendar の定番だと毎日違う人が担当したりしますが、俺が一人で勝手にやってるだけなので、他に Bash Advent Calendar を担当している人はいません。

というわけで、一日で完結しないなんて当たり前w

追記その2

拡張 POSIX シェルスクリプト Advent Calendar 2013とかいうのを見つけたので、有用なのを探している方はこんなのよりそちらへどうぞ。


コメントを投稿

このブログの人気の投稿

シェル操作課題への回答

連想配列 - Bash Advent Calendar - Day 5

今、永福町のピザ戦争が熱い!