【bat】バッチファイルで配列を使う方法完全ガイド|疑似配列・ループ・連想配列・実務パターン

バッチファイルには配列という概念が存在しません。しかし、変数名に添え字を付ける「疑似配列」setlocal enabledelayedexpansion を組み合わせることで、実務で十分に使える配列操作が実現できます。

この記事では、配列の基本的な初期化から要素の追加・削除・検索、ループ処理、連想配列(キーと値のペア)、多次元配列的な実装、さらにはファイルリスト処理やCSVパースといった実務パターンまでを体系的に解説します。

スポンサーリンク

バッチファイルに配列がない理由と疑似配列の考え方

バッチファイル(cmd.exe)の変数はすべて文字列として扱われる単純なキーと値のペアです。変数名に制約がないことを逆手に取り、array[0]array[1]… という名前の変数を作ることで「配列らしく」扱えます。

疑似配列の仕組み
set array[0]=Apple は「array[0] という名前の変数に Apple を入れる」という操作です。本物の配列ではなく、命名規則による見せかけの配列です。インデックスアクセスに !array[%%i]!(遅延展開)が必要になる理由もここにあります。

疑似配列の基本:初期化・参照・更新

array-basic.bat
@echo off
setlocal enabledelayedexpansion

:: ── 初期化 ──────────────────────────────────────
set FRUITS[0]=Apple
set FRUITS[1]=Banana
set FRUITS[2]=Cherry
set FRUITS_LEN=3          &:: 要素数は手動管理

:: ── 参照 ──────────────────────────────────────
echo 0番目: !FRUITS[0]!   &:: Apple
echo 2番目: !FRUITS[2]!   &:: Cherry

:: ── 更新 ──────────────────────────────────────
set FRUITS[1]=Blueberry
echo 更新後: !FRUITS[1]!   &:: Blueberry

endlocal

for /l ループで全要素を処理する

for /l で添え字をカウントアップしながらループするのが最も基本的な反復パターンです。

array-loop.bat
@echo off
setlocal enabledelayedexpansion

set A[0]=Tokyo  & set A[1]=Osaka & set A[2]=Nagoya & set A[3]=Sapporo
set A_LEN=4

:: 0 から A_LEN-1 まで反復
for /l %%i in (0,1,3) do (
    echo [%%i] !A[%%i]!
)

:: 変数で上限指定したい場合(/l は変数不可のため set /a で計算)
set /a A_LAST=%A_LEN%-1
for /l %%i in (0,1,%A_LAST%) do echo !A[%%i]!

endlocal
注意
for /l %%i in (0,1,%A_LAST%) の上限には変数を使えます(パース時に展開されるため)。ただし (0,1,!A_LAST!)(遅延展開)は使えません。

要素の動的な追加と要素数の自動カウント

ループ内で配列に要素を追加しつつ、要素数カウンタも同時に管理するパターンです。

array-push.bat
@echo off
setlocal enabledelayedexpansion

set IDX=0

:: フォルダ内の .log ファイルを配列に格納
for %%f in (C:logs*.log) do (
    set FILES[!IDX!]=%%f
    set /a IDX+=1
)
set FILES_LEN=%IDX%

echo 取得件数: %FILES_LEN%

:: 配列の全要素を表示
set /a LAST=%FILES_LEN%-1
for /l %%i in (0,1,%LAST%) do echo [%%i] !FILES[%%i]!

endlocal

配列の検索(値からインデックスを探す)

array-search.bat
@echo off
setlocal enabledelayedexpansion

set C[0]=Tokyo & set C[1]=Osaka & set C[2]=Nagoya
set C_LEN=3
set TARGET=Osaka
set FOUND=-1

set /a LAST=%C_LEN%-1
for /l %%i in (0,1,%LAST%) do (
    if /i "!C[%%i]!"=="!TARGET!" set FOUND=%%i
)

if %FOUND%==-1 (
    echo %TARGET% は見つかりませんでした
) else (
    echo %TARGET% はインデックス %FOUND% にあります
)

endlocal

要素の削除(前詰めシフト)

バッチの疑似配列は真の削除ができません。対象インデックス以降の要素を前にシフトして詰める方法が一般的です。

array-delete.bat
@echo off
setlocal enabledelayedexpansion

set D[0]=A & set D[1]=B & set D[2]=C & set D[3]=D
set D_LEN=4
set DEL_IDX=1   &:: インデックス 1(B)を削除

:: DEL_IDX 以降を1つ前にシフト
set /a LAST=%D_LEN%-1
for /l %%i in (%DEL_IDX%,1,%LAST%) do (
    set /a NEXT=%%i+1
    :: 二重遅延展開は不可のため call を使う
    call set D[%%i]=%%D[!NEXT!]%%
)
:: 最後の要素をクリア、要素数を1減らす
set D[%LAST%]=
set /a D_LEN-=1

:: 確認: A, C, D
set /a NEW_LAST=%D_LEN%-1
for /l %%i in (0,1,%NEW_LAST%) do echo !D[%%i]!

endlocal

連想配列(キーと値のペア)

変数名をキーとして使うことで、文字列キーによる連想配列を実現できます。

assoc-array.bat
@echo off
setlocal enabledelayedexpansion

:: キー=都市名、値=人口(万人)
set POP[Tokyo]=1396
set POP[Osaka]=275
set POP[Nagoya]=232

:: キーで参照
echo 東京の人口: !POP[Tokyo]! 万人

:: 動的にキーを作って参照
set CITY=Osaka
echo !CITY! の人口: !POP[%CITY%]! 万人

:: for /f で "set POP[" の出力を解析して全エントリを列挙
:: set の出力例: POP[Tokyo]=1396  → tokens=1,2で [=を区切り: %%a=POP, %%b=Tokyo, %%c=1396
for /f "tokens=1,2,3 delims=[]=" %%a in ('set POP[') do (
    echo %%b : %%c 万人
)

endlocal

多次元配列的な実装

変数名に複数の添え字を付けることで、2次元配列的なデータ構造を表現できます。

array-2d.bat
@echo off
setlocal enabledelayedexpansion

:: 3行2列の表(行=ROW, 列=COL)
set M[0][0]=田中  & set M[0][1]=営業
set M[1][0]=佐藤  & set M[1][1]=開発
set M[2][0]=鈴木  & set M[2][1]=総務
set ROWS=3

:: 全行を出力
set /a LAST_ROW=%ROWS%-1
for /l %%r in (0,1,%LAST_ROW%) do (
    echo 氏名: !M[%%r][0]!  部署: !M[%%r][1]!
)

endlocal

実務パターン集

パターン1: ファイルリストを配列に格納して処理する

file-list.bat
@echo off
setlocal enabledelayedexpansion

:: 処理対象ファイルを配列に格納
set N=0
for %%f in (C:data*.csv) do (
    set FILE[!N!]=%%f
    set /a N+=1
)

echo 対象ファイル数: %N%
set /a LAST=%N%-1

for /l %%i in (0,1,%LAST%) do (
    echo 処理中: !FILE[%%i]!
    :: ここで各ファイルへの処理を実行
    :: xcopy "!FILE[%%i]!" D:ackup /Y
)

endlocal

パターン2: CSVの1行を配列に分割してパースする

csv-parse.bat
@echo off
setlocal enabledelayedexpansion

:: CSV行を配列に分割
set LINE=山田太郎,35,東京,営業部
set COL_IDX=0

for /f "tokens=1-4 delims=," %%a in ("!LINE!") do (
    set COL[0]=%%a
    set COL[1]=%%b
    set COL[2]=%%c
    set COL[3]=%%d
)

echo 氏名: !COL[0]!
echo 年齢: !COL[1]!
echo 地域: !COL[2]!
echo 部署: !COL[3]!

endlocal

パターン3: ホワイトリスト照合(配列で許可リストを管理)

whitelist.bat
@echo off
setlocal enabledelayedexpansion

:: 許可する拡張子リスト
set WL[0]=.csv & set WL[1]=.txt & set WL[2]=.log
set WL_LEN=3

set INPUT_EXT=.csv
set ALLOWED=0

set /a WL_LAST=%WL_LEN%-1
for /l %%i in (0,1,%WL_LAST%) do (
    if /i "!WL[%%i]!"=="!INPUT_EXT!" set ALLOWED=1
)

if %ALLOWED%==1 (
    echo %INPUT_EXT% は許可された拡張子です
) else (
    echo %INPUT_EXT% は許可されていません
)

endlocal

よくある問題と対処法

❓ !array[%%i]! と書いても展開されない クリックで開閉

setlocal enabledelayedexpansion が抜けているか、スコープ外になっています。ファイル先頭近くで有効化されているか確認してください。

❓ for /l の上限に変数 %LEN% を使えるか クリックで開閉

for /l %%i in (0,1,%LEN%) は使えます(パース時に展開される)。ただし (0,1,!LEN!)(遅延展開)は構文エラーになるため使えません。set /a LAST=%LEN%-1 で計算した変数を使ってください。

❓ 配列の要素数が動的に決まる場合の対処法 クリックで開閉

格納時にカウンタ変数をインクリメントし、ループ後に set LEN=%IDX% でコピーして保持します。for /l の上限は %LEN%(パース時展開)で問題なく動作します。

よくある質問(FAQ)

Q. バッチファイルでは本当の配列は使えないのですか?
A. バッチファイルには組み込みの配列型はありません。代わりに変数名にインデックスを含めた「疑似配列」を使います:set arr[0]=appleset arr[1]=banana。FORループでfor /L %%i in (0,1,2) do echo !arr[%%i]!のようにアクセスします(DELAYEDEXPANSION必須)。
Q. バッチファイルでスペース区切りの値をFORで処理するのと疑似配列はどちらがよいですか?
A. 少数の値をシンプルに処理するならfor %%i in (apple banana cherry) doのスペース区切りリストが手軽です。インデックスアクセス・要素の追加削除・カウントが必要な場合は疑似配列が適しています。ファイル行ごとの処理にはFOR /Fが向いています。
Q. 配列の要素数(カウント)を取得するにはどうすればよいですか?
A. カウント用の変数を自前で管理します:set count=0し、要素追加ごとにset /a count+=1でインクリメント。または全要素をループして数える:set count=0 & for /L %%i in (0,1,100) do if defined arr[%%i] set /a count+=1。定義されていない変数はif definedでfalseになります。

まとめ

操作 実装パターン
初期化 set A[0]=値
参照 !A[%%i]!(遅延展開必須)
全要素ループ for /l %%i in (0,1,%LAST%)
動的追加 set A[!IDX!]=値 & set /a IDX+=1
検索 ループ内で if /i “!A[%%i]!”==”!TARGET!”
連想配列 set MAP[キー]=値 → !MAP[%KEY%]!
2次元配列 set M[行][列]=値 → !M[%%r][%%c]!

バッチファイルの疑似配列は制約が多いですが、要素数管理・遅延展開・for /l の組み合わせを習得すれば、ファイルリスト処理・CSV パース・ホワイトリスト照合など実務で必要な処理のほとんどをカバーできます。