以前<Windows再起動後にUSB外付けHDDが認識しない原因と対処 - treedown’s Report>再起動時にUSB接続の外付けHDDが認識しないということがあったので、遠隔地のコンピュータの外付けHDDをバッチファイルで監視してみることにしました。
動機
以前<Windows再起動後にUSB外付けHDDが認識しない原因と対処 - treedown’s Report>ということがありました。
この時の(USB接続の)外付けHDDはバックアップストレージなので定期的に動作するバックアップジョブ(によるアクセス)と、バックアップ実行結果のログが送信されることによってUSBドライブの消失や故障といった問題がすぐに知ることができたのですが、そういう定期的なアラートがないサーバのストレージ(ドライブ)だと発見が遅れるということがあるので、外付けHDD(SSD)の状態を検知するバッチファイルをタスクスケジューラで定期実行すればいいのではないかと考えました。
これを実現するには、遠隔地にあるUSB接続の外付けHDDの接続状態をバッチファイルで確認するようにして、それをタスクスケジューラで定期実行すればいいのではないかと考えました。
処理内容としては、接続中なら何もしないけど、その接続が失われている(認識していない)時に何かしらのアクションをするバッチファイルがあれば目的を達成できるかなと思いました。
前提条件として、OSはWindows11 Proで稼働するPCとします。
そのWindows11に接続されたHDDのボリュームラベルはユニーク(一意)で設定されていて、USB接続は1ドライブの外付けHDDを接続している、という条件で今回はバッチを構成します。このため、同じボリュームラベルが複数存在している、という可能性はバッチファイル内で考慮しません。
またアラートはメールアラートにして、メール送信は、以前の<バッチファイルでWindowsサービス監視2023年版 - treedown’s Report>を流用することにしました。
今回は、バッチファイルを構成するために使ったコマンドの内容について解説していきます。(というより、バッチファイルを作るために調べた内容から、実際に使ったものをピックアップしています。)
Get-VolumeとGet-Disk
PowerShellにはボリュームやディスクの接続を確認して、その情報を出力するコマンドレットが用意されています。
※Get-Disk
https://learn.microsoft.com/ja-jp/powershell/module/storage/get-disk?view=windowsserver2025-ps
※Get-Volume
https://learn.microsoft.com/ja-jp/powershell/module/storage/get-volume?view=windowsserver2025-ps
これを使えば、USB接続の外付けHDDでも使える状態かどうかを確認できると考えました。
例えば、「Get-Disk」単体で実行すると、接続中のディスク一覧が画面に表示されます。これをちょっと絞り込むと、
Get-Disk | Select Number, SerialNumber
という感じでSelectでgrepすると表示する列をカスタマイズできます。SerialNumberで監視するディスク個体を特定できると思いました。
今回使おうと思ったのは、
Get-Volume -FileSystemLabel %ボリュームラベル%
これで、指定したボリュームラベルのディスクの情報を表示してくれます。
これらのコマンドレットの実行結果をベースに、判定を入れていくようなバッチを構成してみます。
IF DEFINEDを使う
いままで使ったことなかったのですが、今回「IF DEFINED」という判定を使う必要がありました。
最初はIF EXISTで判定できると思ったのですが、誤判定するケースがあって調べてみたところ、変数内に値が存在するか否かを判断する場合には「IF DEFINED」を使う必要がありました。
「IF DEFINED」は変数をチェックするときに使えるIF文で、変数が定義されていれば真(True)、定義されていない場合には偽(False)を返してくれます。
値が入っているかどうかの判断に便利なIF文なので、使えるシーンは多いと思いました。コマンド実行結果をもとに変数にデータを入れる際、値が設定されてるかどうかで、前段のコマンド実行の結果を判定できるのは便利です。
「IF DEFINED」の動作を確認するために使ったサンプルバッチファイルです。
--------------------------------------------------------------
test.bat
--------------------------------------------------------------
@echo off
set testfile=
if defined testfile (
echo %testfile%は空です。値を入力してください。
)
set /p testfile="値を入力してください > "
if defined testfile (
echo 値に%testfile%が入力されました。
)
set testfile=
--------------------------------------------------------------
これを実行すると、

変数が入力されていない(空っぽの状態)である最初のif defined testfile判定では、偽となるため「echo %testfile%は空です。値を入力してください。」文は実行されずスキップされます。
次の「set /p」文で値を手入力し、変数内に文字列が代入されると、同じ「if defined testfile」の判定でも真となるため、「echo 値に%testfile%が入力されました。」文が実行され、画面に表示される、という動きをします。
変数が空かどうかを判定するIF DEFINED文はこういう時に便利に使えます。
.ps1のtry ~ catch文
バッチファイルを作成していて、どうしても判定が難しい箇所があって、そこは
Powershell -Command "try { %コマンドレット% } catch { %コマンドレット% }"
と、PowerShell側で判定することにしました。.ps1で使う判定文です。
※about_Try_Catch_Finally
https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-7.5
ここの説明を軽く解説すると
try { %コマンドレット% }
で実行したコマンドレットの内容に応じて、
catch { %コマンドレット% }
が実行されます。前述のURLの解説では「tryステートメントでエラーが発生⇒catchブロックを検索して実行する」という動きをするようです。ただこれはリアルタイムな動作でなく、あくまでtryで指定したコマンドレットをすべて実行する前提で、その実行にエラーが含まれている場合、そのエラーに応じてcatchに指定してあるコマンドレットを実行する、という動きをします。※基本的にはエラー発生、即catch行実行ではないので「-ErrorAction Stop」オプションを使用します。
簡単にサンプルを用意してみました。
--------------------------------------------------------------
test2.bat
--------------------------------------------------------------
@echo off
set testfile=test
powershell -Command "try { Get-ChildItem '%testfile%' -ErrorAction Stop } catch{ echo 存在しないファイル「%testfile%」です。 }"
pause
set testfile="C:\temp\test2\test2.bat"
powershell -Command "try { Get-ChildItem '%testfile%' -ErrorAction Stop } catch{ echo 変数testfileには「 %testfile% 」が入っています。 }"
set testfile=
--------------------------------------------------------------
※実行結果:

最初の行で存在しないファイル「test」を変数%testfile%に代入して、次の行で「Get-ChildItem」でその情報を収集しようとします。
当然、存在しないファイルを指定しているため、エラーになるのですが、「-ErrorAction Stop」オプションが動作して、catch以下にあるecho行によって「存在しないファイル「%testfile%」です。」というメッセージが画面出力されます。(※その直後のpauseは処理の区切りをわかりやすくするために入れているだけです。)
一方で、存在するファイル"C:\temp\test2\test2.bat"を変数に代入して、同じ構文の判定をすると、Get-ChildItemでファイル情報を収集できるため、catch以下のecho行は実行されることなく、Get-ChildItemコマンドレットの実行結果が画面に表示されます。
※このサンプルはオプションの「-ErrorAction Stop」がポイントになります。このオプションがないと、エラーとなってもcatch以下で指定した命令が実行されません。
このtry ~ catch文も結構使い勝手がよさそうです。今回は存在の判定に使用しましたが、他の使い方もできそうです。
今回はここまで
今回のバッチファイル実現のために、今まで使ったことのないコマンドレットを使った処理をサンプルで動作確認していました。
次回、これらを組み合わせて、USB接続の外付けHDDを監視するバッチファイルをご報告したいと思います。