シェルスクリプト 中級

Bashの配列:データをまとめて管理する

Bash配列の定義・参照・ループ処理の基礎を学ぶ。複数の値をひとつの変数にまとめて扱うことで、シェルスクリプトをよりシンプルに書けるようになる

複数のファイル名やサーバー名を処理したいとき、変数を file1file2file3 … と増やしていくのは大変です。 Bashの配列を使うと、こうしたデータをひとつの変数にまとめて管理でき、ループと組み合わせると一気に処理できます。

配列の基本

配列の定義

丸括弧 () の中にスペース区切りで値を並べます。

# 配列を定義する
fruits=("apple" "banana" "cherry")

# 変数を展開しながら定義することもできる
servers=("web01" "web02" "db01")

要素を参照する

${配列名[インデックス]} でアクセスします。インデックスは 0から始まります

fruits=("apple" "banana" "cherry")

echo ${fruits[0]}   # apple
echo ${fruits[1]}   # banana
echo ${fruits[2]}   # cherry

${fruits} と中括弧なしで書いたり $fruits とだけ書くと、最初の要素(インデックス0)だけが取り出されるので注意してください。

echo $fruits        # apple(最初の要素だけ)
echo ${fruits[*]}   # apple banana cherry(全要素)

よく使う操作の一覧

操作書き方説明
全要素${配列名[@]} または ${配列名[*]}すべての要素を展開
要素数${#配列名[@]}配列に入っている要素の個数
追加配列名+=("新しい値")末尾に要素を追加
削除unset 配列名[インデックス]特定の要素を削除
fruits=("apple" "banana" "cherry")

echo ${#fruits[@]}   # 3(要素数)

fruits+=("date")
echo ${fruits[@]}    # apple banana cherry date

unset fruits[1]
echo ${fruits[@]}    # apple cherry date

配列とループを組み合わせる

配列の真価はループで引き出せます。for ループで全要素を順番に処理できます。

servers=("web01" "web02" "db01")

for server in "${servers[@]}"; do
  echo "接続確認: $server"
done
接続確認: web01
接続確認: web02
接続確認: db01

ポイント: "${servers[@]}" とダブルクォートで囲むと、要素にスペースが含まれていても正しく分割されます。スペース入りのファイル名などを扱う場合は必ずこの書き方にしましょう。

インデックスを使って処理する

インデックス番号も使いたいときは ${!配列名[@]} でインデックスの一覧を取り出せます。

fruits=("apple" "banana" "cherry")

for i in "${!fruits[@]}"; do
  echo "$i: ${fruits[$i]}"
done
0: apple
1: banana
2: cherry

実用例:複数ファイルの一括バックアップ

配列を使って複数のファイルをまとめてバックアップするスクリプトです。

#!/bin/bash

# バックアップ対象のファイル
targets=("/etc/hosts" "/etc/hostname" "/etc/timezone")
backup_dir="/tmp/backup"

mkdir -p "$backup_dir"

for file in "${targets[@]}"; do
  if [ -f "$file" ]; then
    cp "$file" "$backup_dir/"
    echo "バックアップ完了: $file"
  else
    echo "ファイルが見つかりません: $file"
  fi
done

echo "バックアップ先: $backup_dir"

実用例:コマンドの出力を配列に格納する

mapfile(または readarray)コマンドを使うと、コマンドの出力行を配列に読み込めます。

# カレントディレクトリの .sh ファイルを配列に格納
mapfile -t scripts < <(find . -name "*.sh" -type f)

echo "シェルスクリプトが ${#scripts[@]} 個見つかりました"

for s in "${scripts[@]}"; do
  echo " - $s"
done

サブシェルの出力(<(...))を mapfile -t に渡すことで、改行ごとに要素が分割された配列が作れます。


練習問題

問題1

次の配列 colors を定義し、全要素と要素数をそれぞれ表示するコマンドを書いてください。

colors=("red" "green" "blue" "yellow")

答え:

colors=("red" "green" "blue" "yellow")

# 全要素を表示
echo ${colors[@]}

# 要素数を表示
echo ${#colors[@]}

実行結果:

red green blue yellow
4

問題2

配列 numbers=(10 20 30 40 50) の各要素に対して、2倍した値を出力するスクリプトを書いてください。

答え:

numbers=(10 20 30 40 50)

for n in "${numbers[@]}"; do
  echo $((n * 2))
done

実行結果:

20
40
60
80
100

問題3

配列 files=("report.txt" "data.csv" "script.sh") に対して、ループで各ファイルに touch コマンドを実行し、「report.txt を作成しました」という形式でメッセージを表示するスクリプトを書いてください。

答え:

files=("report.txt" "data.csv" "script.sh")

for file in "${files[@]}"; do
  touch "$file"
  echo "${file} を作成しました"
done

実行結果:

report.txt を作成しました
data.csv を作成しました
script.sh を作成しました

まとめ

  • 配列は 名前=(値1 値2 ...) で定義し、${名前[インデックス]} でアクセスする
  • 全要素を取り出すには ${名前[@]}、要素数は ${#名前[@]}
  • for ループと組み合わせるときは "${名前[@]}" とダブルクォートで囲む
  • コマンドの出力を配列にするには mapfile -t が便利

配列をマスターすると、ループ処理が格段に書きやすくなります。次のステップとして連想配列(declare -A)や、配列を関数に渡す方法にも挑戦してみましょう。