【bat】バッチファイルでUSBデバイス接続を検知して処理する方法

bat

USBメモリや外付けディスクが接続されたタイミングで自動処理を走らせたい場面は多くあります。完全にバッチだけでイベント待受を行うのは難しいため、現実的には「ポーリングで変化を検知する方法」と「PowerShellのイベントをバッチから呼び出す方法」の二本立てが有効です。ここでは純粋なバッチでの再帰監視と、WMIイベントを使った確実な検知の両方を、運用でそのまま使えるテンプレートとして解説します。

方針と前提

USB接続を検知したいだけなら、一定間隔で「現在のリムーバブルドライブの一覧」を取得して前回との差分を取るのが簡単です。より確実に即時検知したい場合は、PowerShellでWin32_VolumeChangeEventを監視し、アクションとしてバッチ処理を呼び出します。いずれの方法でも管理者権限は原則不要ですが、処理内容によっては昇格が必要なケースがあります。

方法1:純バッチでのポーリング検知(リムーバブルドライブの差分監視)

実装は「現在のリムーバブルドライブ一覧」を環境変数に保持し、一定間隔で再取得して新規に現れたドライブを抽出する手順です。論理ディスクの種別2がリムーバブルなので、WMICやPowerShellを使わずにwmic logicaldiskで取得できます。

@echo off
setlocal enabledelayedexpansion

rem 監視間隔(秒)
set "INTERVAL=3"

rem 初期スナップショット(リムーバブル=DriveType=2)
call :GetRemovable CURRENT

echo 初期状態: !CURRENT!
echo 監視を開始します。USBを接続してください。

:LOOP
timeout /t %INTERVAL% /nobreak >nul

call :GetRemovable LATEST
if not "!LATEST!"=="!CURRENT!" (
    call :Diff "!CURRENT!" "!LATEST!" ADDED REMOVED
    if not "!ADDED!"=="" (
        echo 新規検出: !ADDED!
        for %%D in (!ADDED!) do call :OnArrive %%D
    )
    if not "!REMOVED!"=="" (
        echo 取り外し検出: !REMOVED!
        for %%D in (!REMOVED!) do call :OnRemove %%D
    )
    set "CURRENT=!LATEST!"
)
goto :LOOP

rem --- サブルーチン:現在のリムーバブルドライブ一覧を空白区切りで返す ---
:GetRemovable
set "%~1="
for /f "skip=1 tokens=1,2,3 delims== " %%A in ('wmic logicaldisk get DeviceID^,DriveType /value') do (
    if /i "%%A"=="DeviceID" set "_dev=%%C"
    if /i "%%A"=="DriveType" if "%%C"=="2" (
        for %%X in ("!_dev!") do (
            set "_d=%%~X"
            if defined %~1 (set "%~1=!%~1! !_d!") else set "%~1=!_d!"
        )
    )
)
exit /b

rem --- サブルーチン:差分(追加と削除)を計算 ---
:Diff
set "OLD=%~1"
set "NEW=%~2"
set "%~3="
set "%~4="
for %%D in (%NEW%) do (
    echo %OLD% | find /i "%%D" >nul || set "%~3=!%~3! %%D"
)
for %%D in (%OLD%) do (
    echo %NEW% | find /i "%%D" >nul || set "%~4=!%~4! %%D"
)
exit /b

rem --- 接続時の処理をここに記述(例:特定ラベルのUSBだけ処理)---
:OnArrive
set "DRIVE=%~1"
for /f "tokens=2 delims==" %%L in ('wmic volume where "DriveLetter='%DRIVE%'" get Label /value ^| find "="') do set "LABEL=%%L"
echo 到着: %DRIVE% ラベル=%LABEL%

rem 例:ラベルがMYUSBならバックアップフォルダへコピー
if /i "%LABEL%"=="MYUSB" (
    echo バックアップ開始...
    if not exist "%DRIVE%\backup" mkdir "%DRIVE%\backup"
    xcopy "%USERPROFILE%\Documents\*.docx" "%DRIVE%\backup\" /y /d /i >nul
    echo バックアップ完了。
) else (
    echo 対象外のUSBです。何もしません。
)
exit /b

rem --- 取り外し時の処理 ---
:OnRemove
set "DRIVE=%~1"
echo 取り外し: %DRIVE%
rem 必要なら一時ファイルのクリーンアップなどを書く
exit /b

この方法はバッチ単体で完結し、導入が容易です。即時性は間隔次第で変わるため、負荷とレスポンスのバランスでINTERVALを調整します。

方法2:PowerShellのWMIイベントで即時検知し、バッチ処理を呼び出す

即時性と確実性を重視する場合は、PowerShellのRegister-WmiEventWin32_VolumeChangeEventを監視するのが堅実です。バッチはランチャーになり、検知後に指定のバッチ処理を呼び出します。イベントタイプ2が「メディア到着」です。

@echo off
setlocal

set "ACTION_BAT=%~dp0on_usb_arrive.bat"
set "PS=powershell -NoProfile -ExecutionPolicy Bypass"

echo USB接続を監視します。Ctrl+Cで終了します。

%PS% ^
  "$filter = \"SELECT * FROM Win32_VolumeChangeEvent WHERE EventType = 2\"; ^
   Register-WmiEvent -Query $filter -SourceIdentifier USBWatcher | Out-Null; ^
   while ($true) { ^
     $e = Wait-Event -SourceIdentifier USBWatcher; ^
     try { ^
       $drive = ($e.SourceEventArgs.NewEvent.DriveName); ^
       if ([string]::IsNullOrWhiteSpace($drive)) { Remove-Event USBWatcher; continue } ^
       Start-Process -FilePath 'cmd.exe' -ArgumentList '/c \"\"%ACTION_BAT%\" ' + $drive + '\"' -WindowStyle Hidden; ^
     } finally { Remove-Event USBWatcher } ^
   }"

endlocal

呼び出されるon_usb_arrive.batでは、接続先ドライブ文字に応じて処理を行います。ボリュームラベルの判定、コピーや暗号化、バックアップ、スキャンなどの本処理を記述します。

@echo off
setlocal

set "DRIVE=%~1"
if "%DRIVE%"=="" exit /b

for /f "tokens=2 delims==" %%L in ('wmic volume where "DriveLetter='%DRIVE%'" get Label /value ^| find "="') do set "LABEL=%%L"
echo 検知: %DRIVE% ラベル=%LABEL% [%DATE% %TIME%] >> "%~dp0usb_events.log"

rem 例:特定フォルダを同期
if /i "%LABEL%"=="MYUSB" (
  robocopy "%USERPROFILE%\Pictures" "%DRIVE%\Pictures" /MIR /R:1 /W:1 /NFL /NDL /NP >> "%~dp0sync.log" 2>&1
)

endlocal

この構成は即時反応し、USBの到着イベントに対して確実に処理をトリガーできます。サービス化までは行わず、必要時に監視バッチを起動する運用が現実的です。

安全設計と運用のポイント

到着直後はウイルススキャンや自動再生等が並行して動く可能性があるため、処理前に少し待機を入れると安定します。削除や上書きを伴う処理では、対象ドライブとパスの検証、ボリュームラベルやシリアルナンバーの二重チェックを行うと誤爆を防げます。大量コピーはrobocopyの再開機能やミラーオプションを活用し、ログを必ず残すとトラブルシューティングが容易になります。

まとめ

短時間で導入するなら「ポーリング監視」で十分に実用的です。即時性と確実性を求めるなら、バッチをランチャーとして用い、PowerShellのWMIイベントでUSB到着をフックする構成が最適です。いずれの方法でも、対象ドライブの正当性確認、処理中のログ出力、失敗時のリトライやバックアウト手順を組み込むことで、現場運用に耐える堅牢な自動化が実現できます。