bashの配列(Array)の使い方|宣言・追加・ループ・連想配列の実践例

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Linuxtips, シェルスクリプト > bashの配列(Array)の使い方|宣言・追加・ループ・連想配列の実践例

「シェルスクリプトで複数のサーバー名や設定値をまとめて扱いたいのに、毎回変数を個別に定義するのが面倒だ」
そんな悩みを抱えながら、変数1個ずつを地道に書き続けていませんか。

この記事では、bashの配列(Array)の宣言・要素の追加・ループ処理・連想配列(Associative Array)まで、実務で使える形で一通り解説します。
RHEL 9.4 / Ubuntu 24.04 LTS のbash 5系で動作確認済みの手順です。配列をマスターすると、複数ホストへの一括処理やバックアップスクリプトの記述がぐっとシンプルになります。

この記事のポイント

・bash配列は `arr=(a b c)` で宣言し、`${arr[0]}` でインデックス参照できる
・`${arr[@]}` で全要素展開、`${#arr[@]}` で要素数の取得が可能
・連想配列は `declare -A` で宣言し、キーと値のペアで管理できる
・for文との組み合わせでサーバー一括処理・ファイル操作の自動化に直結する


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

bashの配列とは何か?

通常のシェル変数は1つの値しか保持できませんが、配列(Array)を使うと複数の値を1つの変数にまとめて格納できます。

たとえば、複数のサーバーに対して同じ処理を実行したい場合、配列を使わないと次のように書くことになります。

# 配列を使わない書き方(非効率な例) SERVER1="web01.example.com" SERVER2="web02.example.com" SERVER3="db01.example.com" ssh "$SERVER1" "uptime" ssh "$SERVER2" "uptime" ssh "$SERVER3" "uptime"

配列を使えば、サーバーの追加・削除がシンプルになり、ループ処理と組み合わせることで保守性の高いスクリプトを書けます。Linux 基本コマンドの解説と合わせて覚えることで、日常の管理作業を大幅に効率化できます。

bashバージョンの確認方法

配列の全機能を使うにはbash 4.0以上が必要です(連想配列はbash 4.0から対応)。まずバージョンを確認しましょう。

$ bash --version GNU bash, version 5.2.21(1)-release (x86_64-redhat-linux-gnu) Copyright (C) 2022 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

macOS のデフォルト bash は 3.x 系のため、連想配列が使えません。Linux サーバー(RHEL 9 / Ubuntu 24.04)では bash 5.x が標準なので問題ありません。

通常の配列(インデックス配列)の基本操作

1. 配列の宣言と初期化

配列を宣言する方法は主に2つあります。

# 方法1: 括弧で一括定義(最も一般的) SERVERS=("web01" "web02" "db01") # 方法2: インデックスを指定して個別代入 SERVERS[0]="web01" SERVERS[1]="web02" SERVERS[2]="db01" # 方法3: declare -a で明示的に宣言してから代入 declare -a SERVERS SERVERS=("web01" "web02" "db01")

方法1が最もシンプルで現場では最も多く使われます。各要素はスペースで区切ります。要素にスペースが含まれる場合は、ダブルクォートで囲みます。

# スペースを含む要素はダブルクォートで囲む FILES=("/var/log/messages" "/var/log/secure" "/var/log/cron")

2. 要素の参照と全要素の展開

配列の要素を参照するには `${}` 構文を使います。インデックスは0始まりです。

SERVERS=("web01" "web02" "db01") # 特定のインデックスを参照 echo "${SERVERS[0]}" # web01 echo "${SERVERS[1]}" # web02 echo "${SERVERS[2]}" # db01 # 全要素を展開(スペース区切り) echo "${SERVERS[@]}" # web01 web02 db01 # 要素数を取得 echo "${#SERVERS[@]}" # 3 # 最後の要素を参照(bash 4.2以降) echo "${SERVERS[-1]}" # db01

よくあるミス: `$SERVERS` と書くと `${SERVERS[0]}` と同じ意味になります。全要素を展開したいときは必ず `${SERVERS[@]}` を使ってください。

3. 要素の追加・変更・削除

SERVERS=("web01" "web02" "db01") # 末尾に要素を追加(+= 演算子) SERVERS+=("mail01") echo "${SERVERS[@]}" # web01 web02 db01 mail01 # 特定のインデックスの要素を変更 SERVERS[1]="web02-new" echo "${SERVERS[@]}" # web01 web02-new db01 mail01 # 特定のインデックスの要素を削除(unset) unset SERVERS[2] echo "${SERVERS[@]}" # web01 web02-new mail01 echo "${#SERVERS[@]}" # 3(インデックス2が欠けた状態) # 配列全体を削除 unset SERVERS

注意点: `unset` で要素を削除しても、インデックスは詰まりません。インデックス 2 を削除した場合、インデックス 0, 1, 3 が残ります。この「穴」が問題になる場合は、後述のスライス記法や配列の再作成で対処します。

4. 配列のインデックス一覧を取得する

SERVERS=("web01" "web02" "db01") unset SERVERS[1] # インデックス一覧(穴がある場合に便利) echo "${!SERVERS[@]}" # 0 2 # ループでインデックスを使って処理する場合 for i in "${!SERVERS[@]}"; do echo "インデックス $i: ${SERVERS[$i]}" done # インデックス 0: web01 # インデックス 2: db01

配列とfor文の組み合わせ(実践パターン)

1. 全要素をループ処理する(基本形)

配列を使った実務で最もよく使うパターンがfor文との組み合わせです。

#!/bin/bash SERVERS=("web01.example.com" "web02.example.com" "db01.example.com") for SERVER in "${SERVERS[@]}"; do echo "=== $SERVER の稼働確認 ===" ssh -o ConnectTimeout=5 "$SERVER" "uptime" 2>&1 done

ポイントは `"${SERVERS[@]}"` をダブルクォートで囲むことです。スペースを含む要素が1つの要素として正しく扱われます。

2. ファイル一覧をループ処理する

配列にファイルパスを格納してループ処理すると、ls コマンドの基本オプションと組み合わせた場合よりも堅牢な処理が書けます(スペースを含むファイル名でも正しく動作します)。

#!/bin/bash # バックアップ対象のディレクトリ一覧 BACKUP_DIRS=("/etc" "/var/www/html" "/home/webadmin") BACKUP_DEST="/mnt/backup" DATE=$(date +%Y%m%d) for DIR in "${BACKUP_DIRS[@]}"; do # ディレクトリ名をアンダースコア区切りに変換してファイル名に使う DIRNAME=$(basename "$DIR") ARCHIVE="${BACKUP_DEST}/${DIRNAME}_${DATE}.tar.gz" echo "バックアップ中: $DIR -> $ARCHIVE" tar -czf "$ARCHIVE" "$DIR" 2>&1 done echo "全バックアップ完了"

tar コマンドの実用例と組み合わせることで、複数ディレクトリのバックアップを1本のスクリプトで管理できます。

3. 配列のスライス(部分取得)

SERVERS=("web01" "web02" "web03" "db01" "db02") # インデックス1から2要素を取得 echo "${SERVERS[@]:1:2}" # web02 web03 # インデックス3以降を全て取得 echo "${SERVERS[@]:3}" # db01 db02 # 最後の2要素を取得(bash 4.2以降) echo "${SERVERS[@]: -2}" # db01 db02(-2の前にスペースが必要)

連想配列(Associative Array)の使い方

1. 連想配列の宣言と初期化

連想配列はインデックスに数値ではなく文字列を使える配列です。必ず `declare -A` で宣言してから使います。

# 連想配列の宣言(-A オプション必須) declare -A SERVER_ROLES # キーと値を設定 SERVER_ROLES["web01"]="Webサーバー" SERVER_ROLES["web02"]="Webサーバー" SERVER_ROLES["db01"]="データベースサーバー" SERVER_ROLES["mail01"]="メールサーバー" # または宣言と同時に初期化 declare -A SERVER_ROLES=( [web01]="Webサーバー" [web02]="Webサーバー" [db01]="データベースサーバー" [mail01]="メールサーバー" )

2. 連想配列の参照・一覧取得

declare -A SERVER_ROLES=( [web01]="Webサーバー" [db01]="データベースサーバー" [mail01]="メールサーバー" ) # 特定のキーを参照 echo "${SERVER_ROLES[web01]}" # Webサーバー # 全キーを取得(! を使う) echo "${!SERVER_ROLES[@]}" # web01 db01 mail01 # 全値を取得 echo "${SERVER_ROLES[@]}" # Webサーバー データベースサーバー メールサーバー # 要素数を取得 echo "${#SERVER_ROLES[@]}" # 3

3. 連想配列をループ処理する

#!/bin/bash declare -A SERVER_ROLES=( [web01.example.com]="Webサーバー" [db01.example.com]="データベースサーバー" [mail01.example.com]="メールサーバー" ) echo "=== サーバー一覧と稼働確認 ===" for HOST in "${!SERVER_ROLES[@]}"; do ROLE="${SERVER_ROLES[$HOST]}" echo -n "[$ROLE] $HOST: " if ping -c 1 -W 2 "$HOST" &>/dev/null; then echo "応答あり" else echo "応答なし(要確認)" fi done

4. 連想配列でキーの存在確認

連想配列のキーが存在するかどうかは、`-v` オプション付きの条件式で確認します。

declare -A SERVER_ROLES=( [web01]="Webサーバー" [db01]="データベースサーバー" ) # キーが存在するかチェック if [[ -v SERVER_ROLES[web01] ]]; then echo "web01 は登録済み: ${SERVER_ROLES[web01]}" else echo "web01 は未登録" fi # 出力: web01 は登録済み: Webサーバー # 未登録キーの場合 if [[ -v SERVER_ROLES[proxy01] ]]; then echo "proxy01 は登録済み" else echo "proxy01 は未登録" fi # 出力: proxy01 は未登録

実務で役立つ配列の活用パターン

1. コマンドの実行結果を配列に格納する

コマンドの出力結果を配列に格納して処理することは実務で頻繁に登場します。

#!/bin/bash # /var/log配下のerrorを含むログファイルを配列に格納 mapfile -t ERROR_LOGS < <(find /var/log -name "*error*" -type f 2>/dev/null) echo "エラーログファイル数: ${#ERROR_LOGS[@]}" for LOG in "${ERROR_LOGS[@]}"; do echo "確認中: $LOG" tail -5 "$LOG" echo "---" done

mapfile(または readarray)は標準入力の各行を配列の要素として読み込むbash組み込みコマンドです。`-t` オプションで末尾の改行を除去します。

2. 配列で条件分岐と組み合わせた一括チェックスクリプト

#!/bin/bash # 必須ポートの一括チェック declare -A REQUIRED_PORTS=( [22]="SSH" [80]="HTTP" [443]="HTTPS" [3306]="MySQL" ) TARGET_HOST="192.168.1.100" FAILED=() for PORT in "${!REQUIRED_PORTS[@]}"; do SERVICE="${REQUIRED_PORTS[$PORT]}" if nc -z -w2 "$TARGET_HOST" "$PORT" 2>/dev/null; then echo "[OK] $SERVICE (port $PORT)" else echo "[NG] $SERVICE (port $PORT) - 接続不可" FAILED+=("$PORT:$SERVICE") fi done if [[ ${#FAILED[@]} -gt 0 ]]; then echo "" echo "=== 接続失敗 ${#FAILED[@]} ポート ===" for F in "${FAILED[@]}"; do echo " $F" done exit 1 fi echo "全ポート疎通確認OK"

3. 配列を関数に渡す

配列を関数に渡すときは少し注意が必要です。直接渡すのではなく、ナームリファレンス(`declare -n`)またはグローバル変数経由で渡すのが実務上の定番です。

#!/bin/bash # 方法1: ナームリファレンスを使って配列を渡す(bash 4.3以降推奨) function print_array() { declare -n arr_ref="$1" # 呼び出し元の変数名を参照 for item in "${arr_ref[@]}"; do echo " - $item" done } SERVERS=("web01" "web02" "db01") echo "サーバー一覧:" print_array SERVERS # 方法2: 配列を展開して渡す(全要素を位置パラメータとして受け取る) function check_hosts() { local -a hosts=("$@") for host in "${hosts[@]}"; do ping -c 1 -W 1 "$host" &>/dev/null && echo "$host: 応答あり" || echo "$host: 応答なし" done } HOSTS=("localhost" "127.0.0.1") check_hosts "${HOSTS[@]}"

トラブルシュートとよくあるミス

1. IFS(フィールド区切り文字)の影響に注意

スペースを含む要素を正しく扱うため、ダブルクォートを忘れないことが最重要です。

FILES=("file with spaces.log" "normal.log" "another file.txt") # NG: ダブルクォートなしだとスペースで分割される for f in ${FILES[@]}; do echo "$f" # "file", "with", "spaces.log" と3つに分割されてしまう done # OK: ダブルクォートあり for f in "${FILES[@]}"; do echo "$f" # "file with spaces.log" と正しく1要素として扱われる done

2. `declare -A` なしで連想配列を使うとインデックス配列になる

# NG: declare -A を省略した場合 MAP[web01]="Webサーバー" # 文字列キーが数値変換されてしまう echo "${MAP[0]}" # 予期しない動作 # OK: 必ず declare -A を先に記述する declare -A MAP MAP[web01]="Webサーバー" echo "${MAP[web01]}" # Webサーバー

3. unset後のインデックスの穴

SERVERS=("web01" "web02" "db01") unset SERVERS[1] # インデックスに穴があるため注意 echo "${SERVERS[*]}" # web01 db01 echo "${#SERVERS[@]}" # 2 echo "${SERVERS[1]}" # 空(インデックス1は削除済み) echo "${SERVERS[2]}" # db01 # 穴を詰めたい場合は配列を再作成する SERVERS=("${SERVERS[@]}") echo "${SERVERS[0]}" # web01 echo "${SERVERS[1]}" # db01(詰まった)

4. サブシェルで変更した配列は親に反映されない

SERVERS=("web01") # パイプ右辺はサブシェルで実行されるため、追加が親に反映されない echo "web02" | while read -r s; do SERVERS+=("$s") done echo "${#SERVERS[@]}" # 1(追加されていない) # 対策: プロセス置換を使う while read -r s; do SERVERS+=("$s") done < <(echo "web02") echo "${#SERVERS[@]}" # 2(正しく追加された)

本記事のまとめ

bashの配列は、シェルスクリプトで複数の値を効率よく管理するための重要な機能です。

やりたいこと 書き方
配列の宣言 arr=("a" "b" "c")
インデックス指定で参照 ${arr[0]}
全要素を展開 ${arr[@]}
要素数を取得 ${#arr[@]}
全インデックスを取得 ${!arr[@]}
末尾に要素を追加 arr+=("d")
要素を削除 unset arr[2]
スライス(n番目からm個) ${arr[@]:n:m}
コマンド出力を配列に格納 mapfile -t arr < <(command)
連想配列の宣言 declare -A map
連想配列のキー一覧 ${!map[@]}
連想配列のキー存在確認 [[ -v map[key] ]]

配列を使いこなすと、複数ホストの一括処理・必須ポートチェック・バックアップの一括管理など、実務でよく遭遇するシナリオをすっきり書けるようになります。まずは小さなスクリプトで `arr=()` の形から試してみてください。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Linuxサーバー構築入門マニュアル(図解60P)を無料でお渡ししています。

>> Linuxサーバー構築入門マニュアル(図解60P)を無料で受け取る

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。