【bat】バッチファイルが途中で止まる原因と対処法

【bat】バッチファイルが途中で止まる原因と対処法 bat

バッチファイルが「途中で止まる」とき、原因は一つではありません。

エラーメッセージが出て止まるケースもあれば、何も表示されず固まったように見えるケースもあり、さらに“止まっているつもりで実は待っているだけ”というパターンも混ざります。

実務では、止まった瞬間に状況を誤認すると切り分けが長期化しやすいので、まず止まった原因の種類を分類してから対処に入るのが最短です。

「止まる」の正体を最初に切り分ける

止まる原因は大きく分けて、ユーザー入力待ち、外部プロセスの終了待ち、ネットワークやファイルI/O待ち、権限やロック待ち、無限ループや条件分岐ミス、コマンドが暗黙に停止する仕様、のいずれかに収束します。

最初の一手として、処理がどこまで進んだのかを確定させます。

ダブルクリック実行で画面が消える・状況が追えない場合は、ログに落として再現させるのが実務では最優先です。


@echo off
setlocal

set "LOG=%~dp0run.log"
call :main > "%LOG%" 2>&1
exit /b %ERRORLEVEL%

:main
echo start
rem ここに本処理を書く
echo end
exit /b 0

ログに start しか残っていないのか、途中まで出ているのかで、止まっている地点が確定します。

原因1:pauseやchoiceなどの入力待ちで止まっている

実務で意外に多いのが、開発時に入れた pause や choice が残っていて、入力待ちで停止しているパターンです。

また、外部ツールが対話モードで入力を求めている場合も同様に止まります。

ログを見るとそれらしい文言が残るので、止まっている地点の直前を確認します。


choice /c YN /m "続行しますか?"
if errorlevel 2 exit /b 1

運用バッチで入力待ちを使う場合は、タイムアウト付きの設計にするか、対話バッチと自動実行バッチを分離する方が事故が減ります。

原因2:コンソール上で「選択状態」になっている

見落とされやすいのが、コマンドプロンプト(コンソール)のテキストがドラッグされて選択状態になっており、結果として処理が止まったように見えるケースです。

Windows のコンソールは、選択状態になると一部の挙動が停止し、バッチが進んでいないように見えます。

特に、マウスでウィンドウをクリックした際に意図せず選択状態に入ることがあり、現場では「急に止まった」「固まった」と誤認されがちです。

対処としては、まず Enter か Esc を押して選択状態を解除し、解除後に処理が進むかを確認します。

再発が多い運用では、作業手順として「実行中にコンソールをドラッグ選択しない」を周知し、必要なら別ウィンドウでログを見る運用に寄せるのが安定します。

原因3:startの使い方が原因で処理が戻ってこない

start は別プロセス起動に使えますが、/wait を付けると終了待ちになり、付けないと即時に次へ進みます。

「止まる」という相談の中には、/wait を付けた結果として外部プロセスが終了せず、待ち続けているケースが多く含まれます。

また、start の第1引数はタイトル扱いになるため、パス指定を誤ると想定外挙動になります。


rem タイトルを明示して誤解を避ける
start "" /wait "C:\path\tool.exe" -arg1 -arg2

止まり方が「待機」か「固まり」かを区別するには、タスクマネージャで tool.exe が生きているかを見るのが早いです。

原因4:ネットワーク待ち・共有フォルダ待ちで止まる

社内バッチで多い実務系の停止原因が、共有フォルダやネットワーク越しのアクセスが固まるパターンです。

存在確認やコピーが一見止まって見えますが、実際は OS 側でリトライやタイムアウト待ちが走っています。

ログに何も出ず、特定行で止まるなら、ネットワーク経由のパスが絡んでいないかを疑います。

対策としては、ネットワーク依存の処理を手前でチェックし、失敗時は早期終了させる設計に寄せます。


set "SHARE=\\server\share\path"

dir "%SHARE%" > nul 2>&1
if %ERRORLEVEL% neq 0 (
  echo 共有フォルダにアクセスできません: %SHARE%
  exit /b 1
)

また、コピーの途中で止まる場合は、コピー先の容量不足や権限だけでなく、相手側の一時的なI/O詰まりも原因になります。

原因5:ファイルロックで待っている

ログやCSVを追記する処理、別プロセスが開いているファイルへの操作で「止まる」ように見えることがあります。

エラーにならず待機が続く場合、ファイルロックや、セキュリティ製品のスキャン待ちが疑われます。

対処は、対象ファイルを別名に出力してから置換する、ロックされやすいファイルへの追記を避ける、などの設計変更が実務的です。


set "OUT=%~dp0out.tmp"
set "FINAL=%~dp0out.csv"

rem まず別名で生成
somecommand > "%OUT%"

rem 最後に置換
move /y "%OUT%" "%FINAL%" > nul

この方式は、途中成果物が壊れた状態で残りにくいメリットもあります。

原因6:for /fが入力待ちになって止まる

for /f はファイル読み込みやコマンド出力のパースに便利ですが、入力ソース指定を誤ると標準入力待ちになり、無言で止まります。

特に、ファイルが存在しないのに for /f で読もうとしている場合、期待と違う動作になることがあります。

対策としては、必ず事前に exist でファイル存在を確認してから読みます。


set "FILE=%~dp0data.txt"
if not exist "%FILE%" (
  echo 読み込み対象が存在しません: %FILE%
  exit /b 1
)

for /f "usebackq delims=" %%A in ("%FILE%") do (
  rem 処理
)

原因7:set /pが標準入力待ちになって止まる

set /p は入力受付に使いますが、リダイレクトやパイプの中で呼ばれると、意図せず入力待ち状態になります。

バッチを他のバッチから呼ぶ、PowerShellから呼ぶ、タスクスケジューラで呼ぶなど、実務の実行形態で発生しやすいポイントです。

対話入力が不要な自動実行バッチでは、set /p を使わない設計に寄せた方が安全です。

原因8:無限ループや条件分岐ミスで抜けられない

止まっているように見えて、実際は同じ箇所を回り続けているケースです。

エラーが出ないのに CPU 使用率が上がる、ログが増え続ける、ディスクアクセスが続く場合は無限ループを疑います。

無限ループの典型は、カウンタ更新忘れ、条件が常に真、goto が戻り続ける、遅延展開の誤用です。


@echo off
setlocal enabledelayedexpansion

set /a i=0
:loop
set /a i+=1
if !i! gtr 10 goto :done
goto :loop

:done
echo done

遅延展開を使わず %i% を参照すると、ブロック内で値が固定されて抜けられないケースがあるため注意が必要です。

原因9:外部コマンドがハングしている

バッチ自身ではなく、呼び出した外部コマンドが停止している場合、バッチは「待ち」になります。

このとき、ERRORLEVEL を見ても戻ってこないので判定できません。

実務では、外部コマンドを呼ぶ場合にタイムアウト設計を入れるのが有効です。

Windows標準だけで完結させるなら、プロセス監視と強制終了を組み合わせる設計が現実的になります。


@echo off
setlocal

set "TOOL=C:\path\tool.exe"

start "" "%TOOL%"
timeout /t 30 /nobreak > nul

tasklist | find /i "tool.exe" > nul
if %ERRORLEVEL% equ 0 (
  echo 規定時間内に終了しないため停止します
  taskkill /f /im tool.exe > nul 2>&1
  exit /b 1
)

exit /b 0

強制終了は最終手段ですが、夜間バッチなど「止まる=致命的」な運用では必要になる場面があります。

原因10:exit /bの戻り値やcallの設計ミスで止まって見える

call :label を多用していると、exit /b の位置や goto の書き方を誤って、意図せず同じ処理に戻り続けることがあります。

特に、サブルーチンの末尾で exit /b を書かずに fall-through すると、別ラベルへ流れ込むことがあり、これが「止まる」「変な挙動」の原因になります。

サブルーチンは必ず入口と出口を固定し、出口で明示的に exit /b を返す構造にします。


call :work
if %ERRORLEVEL% neq 0 exit /b 1
exit /b 0

:work
rem 本処理
exit /b 0

止まる問題を最短で解決する現場的アプローチ

実務で滞在時間が伸びるタイプの問題は、原因が一発で当たらないため、再現性を上げる仕組みが重要です。

まずログ出力で止まる地点を確定し、次にその直前の行へ「開始」「終了」の印を入れて範囲を狭めます。


echo [STEP1] start
rem 処理
echo [STEP1] end

echo [STEP2] start
rem 処理
echo [STEP2] end

この手順で、止まる原因が入力待ちなのか、外部プロセス待ちなのか、I/O待ちなのか、ループなのかを短時間で分類できます。

まとめ

バッチファイルが途中で止まる原因は、入力待ち、コンソールの選択状態、外部プロセス待ち、ネットワーク・共有フォルダ待ち、ファイルロック、for /f や set /p の標準入力待ち、無限ループ、外部コマンドのハング、call/exit設計ミスなどに集約されます。

最短で解決するには、まずログで止まる地点を確定し、止まり方を分類してから対策に入ることが重要です。

この入口が整うと、個別テーマとして「for /f が止まる原因」「start /wait の落とし穴」「遅延展開でループが抜けない理由」などへ深掘りしやすくなり、実務の停止トラブルを再現性高く潰せるようになります。