配列変数の展開
特定の要素に対する文字列展開は、普通の変数の文字列展開と一緒なので省略。${var[@]:-str}
var が存在し1個以上あれば ${var[@]} に同じ、そうでなければ str になる。var の中身を見て、それが空白文字だったら str に置きかわる、という動作をするわけではない。
${var[@]-str}
var が存在し1個以上あれば ${var[@]} に同じ、そうでなければ str になる。中身がない空の配列でも str に置き代わってしまうという残念な動作。仕様としては未定義っぽいけど。
${var[@]:?str}, ${var[@]:+word}
上と同様。
${var[@]:=str}
エラーになる。正直、エラーにならない場合、どんな動作をすべきか思いつかないので、妥当な仕様だと思う。
${var[@]:offset}, ${var[@]:offset:length}
offset 番目から length 個の要素を取り出す。実は同様に ${@:offset} とか ${@:offset:length} で、位置パラメータを部分的に取得できるんだけど、普通は shift 使えということで目にすることはまずないでしょう。残念ながら、各要素の部分文字列展開をしたい場合には、for ループで回すしかない。
${#var[@]}
配列の要素数になる。「各要素の文字列の長さ」が返ってくるわけではない。残念だけど、じゃぁ、配列の要素数を返すのに他に適当な記法があるかといえば、たしかにこれがベストと言わざるを得ないでしょう。
${var[@]#str}, ${var[@]##str}, ${var[@]%str}, ${var[@]%%str}
それぞれの要素に対して、先頭または末尾にマッチする文字列が削除、その結果を返します。
${var[@]/pattern/str}, ${var[@]//pattern/str}
それぞれの要素に対して、パターンにマッチした文字列を置換して、その結果を返します。
${var[@]^pattern}, ${var[@]^^pattern}, ${var[@],pattern}, ${var[@],,pattern}
それぞれの要素に対して、大文字、小文字変換変換した結果を返します。
というわけで、
- 「変数が存在するかどうか、または空文字列か」をチェックする系のは動作はするけど : の有無が影響しないのでつまらない。
- 数値を返す、または数値を引数に取る系のは、各要素に対して何か操作をするのではなく、配列全体に対する動作になる
- 文字列置換系は各要素に対しての操作になる
とまとめられるかと思います。
[[ X -gt Y ]] と [ X -gt Y ] の互換性
正直、undocumented なので言及するだけ時間の無駄な気がしますが、現状、[[ ]] の場合は X Y ともに算術式評価がされるようです。なので$ X=2
$ Y=1
$ [[ X -gt Y ]]; echo $?
0
$ [ X -gt Y ]; echo $?
bash: [: X: 整数の式が予期されます
2
と非互換の動作をします。算術式評価であれば、ちゃんと
(( X > Y ))
と書く方法があるので、"-gt" と test と同じ名前を使うのであれば、やっぱり test と同じような動作をすべきなんじゃないかと思うわけです。
declare コマンドと変数宣言
さてここまでずっと変数の宣言として declare コマンドを使ってきましたが、何回か書いてあるとおり、これらは内部「コマンド」であって bash の文法の一部ではありません。キバヤシなら「な、何だってー−!」と驚くところです。で、何がポイントかというと、あるコマンドの実行結果を変数に代入する場合に問題になります。
- $?は最後のコマンドの実行結果を返す
- declareコマンドが実行される前に $() のコマンド展開が行われる
ということで
$ declare shadow=$(cat /etc/shadow 2>/dev/null)実際のところ、declare コマンドでこれが問題になるケースは稀だと思いますが、その兄弟的な local コマンドでありがちなミスとして
$ echo $?
0
$ unset; declare shadow
$ shadow=$(cat /etc/shadow 2>/dev/null)
$ echo $?
1
function foo(){みたいなのがあると思います。mktemp コマンドが失敗しても local コマンドは成功してしまうので、この if 文の評価は常に偽になります。
local tmp=$(mktemp foo.XXXXXX)
if [[ $? -ne 0 ]]; then
...
}
というわけで
local var=valueと書くと短く書けるのはいいのですが、右辺にコマンド展開がある場合は
local varと書く癖を付けておきましょう。
value=$(...)
あと、これを書く動機として
- declare -i -A または declare -i -a で右辺が算術評価されない
というバグがあります。これは
declare -i -a fooのように宣言と代入を分けることで回避できます。これ自体で困ることはそうそうないとは思いますが、こういうバグを見ると「どうやったらこんなバグ生じさせられるんだよ」とか「こんなところでバグるソフトとか信頼性どうなのよ」とか、でもきっと中の人は大変なんだろうなとか、いろいろと空想できて楽しいと思います。
foo=("1 + 2")
というわけで今日の結論。やっぱり配列とか連想配列とか使うんだったら他のLL使いましょうw
0 件のコメント:
コメントを投稿