Linuxのシェルでの配列の利用と応用例

Bash arrayは複数の値を一variableに保持し、quoted “${array[@]}”で各要素の境界を保てます。POSIX shのportable機能ではなくBash固有です。空要素、space、sparse index、associative key、順序を理解し、command outputをunquotedでarray化しません。

結論は「順序付きlistはdeclare -a、key/valueはdeclare -Aを使い、assignmentと展開を常にquoteします。fileの各lineはmapfile -tまたはwhile readで読み、要素数・index・空要素をtestします」です。

目次

indexedとassociativeを選ぶ基準

Bashではsubscriptに整数を使うindexed arrayと文字列keyを使うassociative arrayがあります。indexed arrayは0からの連続を保証せず、要素削除後はsparseになります。associative arrayのiteration順を業務順序として依存しません。

  • scriptがBashで実行されること
  • indexed順序かkey lookupのどちらが必要か
  • empty element・space・newlineを保持するか
  • input sizeと最大要素数
  • set -u環境でempty arrayを扱う方法

declare -p arrayでtypeと内容を確認し、要素数は${#array[@]}、indexは${!array[@]}で取得します。表示時も機密値を含まないsampleに限定します。command substitutionをunquotedでarray assignmentへ入れる方法はword splittingとglobを起こすため避けます。

Bash arrayはindexedとassociativeの二種類で、POSIX shには同じ機能がありません。scriptのshebangと実際のinterpreterを確認し、declare -pで型、${#array[@]}で要素数、${!array[@]}でindexまたはkeyを読みます。0要素、空文字一要素、sparse indexを区別し、入力がline、NUL区切り、CSV、JSONのどれかに応じて読込方法を選びます。

要素境界を壊さず代入する

literal listはitems=(“one” “two words” “”)とquoteして作ります。fileのlineはmapfile -tへredirectし、NUL-separated pathはread -d ”等を使います。functionへ渡すときは”${items[@]}”で展開し、受け側は”$@”として再構築します。

indexed arrayを作る

items=("alpha" "two words" "")
printf 'count=%s\n' "${#items[@]}"
printf '<%s>\n' "${items[@]}"

quoted [@]でspaceとempty elementを個別要素として渡します。

要素を追加

items+=("omega")
printf 'last=%s\n' "${items[-1]}"

negative indexのsupport/versionを対象Bashで確認し、portable性が必要なら明示indexを使います。

indexと値をloop

for i in "${!items[@]}"; do
  printf 'index=%s value=<%s>\n' "$i" "${items[i]}"
done

sparse indexでも実在indexを列挙できます。

associative array

declare -A ports=([http]=80 [https]=443)
printf 'https=%s\n' "${ports[https]}"

keyの許可範囲を決め、iteration orderへ依存しません。

fileを一line一要素で読む

mapfile -t lines < ./input.txt
printf 'lines=%s\n' "${#lines[@]}"

-tはdelimiter newlineを取り除き、巨大fileではmemory上限を確認します。

indexed arrayはitems=(“alpha beta” gamma)のように各要素をquoteし、展開は”${items[@]}”を使います。associative arrayはdeclare -A mapを先に宣言し、許可されたkeyで値を設定します。line単位の小規模入力はmapfile -t、巨大fileはwhile readで逐次処理します。外部commandの出力をarr=($(command))で受けず、delimiterを保つ方法を選びます。

追加・iterationと[@]・[*]・sparse indexを理解する

“${array[@]}”は各要素を別wordとして展開し、”${array[*]}”はIFS先頭文字で一つのwordへ結合します。quoteしない展開は各要素をさらにword splittingとfilename expansionへかけます。${#array[@]}はelement countで、最大index+1とは限りません。

  • Bash arrayはPOSIX shでportableな機能ではない
  • indexed arrayのsubscriptはarithmetic expressionとして評価される
  • associative arrayはdeclare -Aで宣言する
  • quoted [@]は各elementを別wordとして保持する
  • mapfile/readarrayはstandard inputのlineをindexed arrayへ読む

quoteされた${array[@]}は各要素を別wordとして保持し、${array[*]}はIFS先頭文字で一つのwordへ結合します。unquoted展開は各値をさらにword splittingとglobへ通します。indexed arrayのsubscriptはarithmetic expressionとして評価され、associative arrayのkeyとは意味が異なります。要素数は最大index+1とは限らず、unset後のsparse arrayでは特に違います。

外部inputとkeyを安全に扱う

untrusted keyをarithmetic subscriptへ直接入れず、indexed/associativeの型を明示します。大量fileをmapfileでmemoryへ全部読む前にsize上限を確認します。array要素をevalやshell codeへ連結せず、command argumentとしてquoted展開します。

  • unquoted command substitutionで空白とglobを壊す
  • “${array[*]}”と”${array[@]}”を同じだと思う
  • element countをlast indexとみなす
  • associative arrayのiteration順を保証とする
  • shでBash array syntaxを実行する

untrusted値をindexed subscriptへ直接使わず、整数形式と範囲を検証します。associative keyも許可文字と長さを制限し、evalで配列参照を組み立てません。mapfileは入力全体をmemoryへ載せるためsize上限を確認します。secretを含むarrayをdeclare -pやset -xでログへ出さず、function引数へ渡す場合もprocess環境やcommand line露出を避けます。

空arrayと特殊文字をtestする

0要素、empty一要素、space、newline、wildcard、leading dash、sparse index、associative keyをtestします。declare -p、element count、index list、functionへ渡ったargcを比較し、意図しない再分割がないことを確認します。

  1. Bash interpreterとarray typeを明示した
  2. quoted [@]でelement境界を保持した
  3. empty・space・sparse・keyをtestした
  4. input sizeとuntrusted subscriptを制限した

空配列、空文字要素、space、tab、newline、wildcard、leading dash、sparse index、重複keyを含むfixtureで、要素数と各値の境界をprintf ‘<%s>\n’等で確認します。”${array[@]}”をfunctionへ渡したときのargcを比較し、保存・再読込で欠落しないかtestします。shで起動した場合は早期に明確なerrorを出すことも確認します。

用途に合うdata structureを選ぶ

順序付き少数listはindexed、name lookupはassociative arrayを使います。巨大data、nested structure、JSON、並列更新が必要ならshell arrayで無理に表現せず専用language/parserへ移します。

順序付きの小規模listはindexed、name lookupはassociative arrayが向きます。nested object、巨大data、JSON、複雑なvalidationが必要ならshell arrayで無理に表現せずjqやPython等を使います。POSIX portable scriptが要件ならBash arrayを避け、改行やNUL区切りstreamと明確なparserを設計します。

公式情報・参考資料

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次