この記事でわかること
- VBAのShell関数で外部プログラム(exe)やバッチファイルを起動できる
- WScript.Shell(WshShell)で終了待ち・終了コード取得・標準出力の読み取りができる
- Shell関数とWScript.Shellの使い分けが明確になる
対象: Excel 2016以降 / Microsoft 365、Windows 10/11
どんな場面で使う?
- 毎朝のバッチファイル実行をExcelのボタン1つで自動化したいとき
- 外部ツールで前処理した結果をExcelに取り込む一連の流れを組みたいとき
- PowerShellスクリプトやコマンドをVBAから定期実行したいとき
- バッチ実行の結果(終了コード)をログに記録して管理したいとき
—
完成イメージ(Before / After)
Before(手作業):
- Excelでレポートを開く
- エクスプローラーでバッチファイルをダブルクリック
- コマンドプロンプトの完了を待つ
- 結果ファイルをExcelで手動で開く
After(VBAで自動化):
- Excelのボタンを押す
- バッチファイルが自動実行→完了を待つ→結果をExcelに取り込む→ログ記録
ボタン1つで「外部コマンド実行→結果取り込み」まで一気通貫で済む。
—
自分も以前、毎朝出社してからExcelでレポートを開いて、手動でバッチファイルを実行して、結果をまた手作業でExcelに取り込んでいた。この「Excel→バッチ→Excel」の往復が地味にめんどくさかった。VBAからShellでバッチファイルを直接呼び出せるようにしてからは、ボタン1つで前処理から集計まで一気に終わるようになった。ExcelとコマンドプロンプトやバッチファイルをVBAでつなげると、できることが一気に広がる。この記事で、その第一歩を踏み出してもらえればうれしい。
VBAのShell関数またはWScript.Shellを使えば、Excelからワンクリックで外部プログラムやコマンドを実行できる。
なお、実行するファイルやフォルダの存在を事前にチェックしたい場合は ファイルやフォルダの存在を確認してから処理する方法 を参照。
—
実行前の準備
バックアップを取る
外部コマンドの実行は、ファイルの作成・上書き・削除など予期しない結果を招く可能性がある。必ずファイルのコピーを別フォルダに保存してから実行する。
注意: Shell関数やWScript.Shellは任意の外部プログラムを実行できる強力な機能です。信頼できないコマンドや不明なバッチファイルは絶対に実行しないでください。テスト時は無害なコマンド(notepad等)から始めてください。
Excelをマクロ有効ブック(.xlsm)で保存する
拡張子が .xlsx のままだとマクロが保存できない。
- 「ファイル」→「名前を付けて保存」
- ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
- 保存
—
Shell関数とWScript.Shellの使い分け
VBAから外部プログラムを実行する方法は主に2つある。まずはこの比較表で全体像をつかむ。
| 項目 | Shell関数(VBA標準) | WshShell.Run | WshShell.Exec |
|---|---|---|---|
| 参照設定 | 不要 | 不要(CreateObject) | 不要(CreateObject) |
| 同期/非同期 | 非同期のみ | 選択可能 | 同期的に出力読み取り |
| 終了コード取得 | 不可(プロセスIDのみ) | 可能(同期時) | 可能 |
| 標準出力の取得 | 不可 | 不可 | 可能 |
| ウィンドウ制御 | vbHide等で制御 | 0〜10で制御 | 常に非表示 |
| 用途 | 起動するだけ | 終了待ち・終了コード | コマンド出力の読み取り |
結論: 「起動するだけ」ならShell関数。「終了を待ちたい」「終了コードが欲しい」ならWshShell.Run。「コマンドの出力を読み取りたい」ならWshShell.Exec。
—
手順(コピペ → 実行まで約5分)
VBE(コードを書く画面)を開く
- Excelで
Alt + F11を押す
標準モジュールを挿入する
- VBEのメニュー →「挿入」→「標準モジュール」
コードを貼り付けて実行する
- コードウィンドウに、下のコードをそのままコピペする
Alt + F8→ マクロ名を選んで「実行」
ボタンに割り当てれば毎回Alt+F8を押さなくて済む。方法は マクロをボタン1つで実行する方法 を参照。
—
コード(最小版)– Shell関数でメモ帳を開く
まずは最もシンプルな例。VBAからメモ帳(notepad.exe)を起動する。
'============================================================
' ■ Shell関数でメモ帳を開く(最小版)
' → VBAから外部プログラムを起動する最小限のコード
'============================================================
Sub OpenNotepad()
Dim pid As Double
pid = Shell("notepad.exe", vbNormalFocus)
'← pid にはプロセスID(Windowsが起動中のプログラムに付ける番号)が入る
MsgBox "メモ帳を起動しました(プロセスID: " & pid & ")", vbInformation
End Sub
書き換えポイント
| 変数/引数 | 説明 | 初期値 |
|---|---|---|
"notepad.exe" |
実行するプログラムのパス | notepad.exe(メモ帳) |
vbNormalFocus |
ウィンドウの表示方法 | 通常表示でフォーカスあり |
ウィンドウスタイルの一覧
| 定数 | 値 | 動作 |
|---|---|---|
vbHide |
0 | ウィンドウ非表示(バックグラウンド実行) |
vbNormalFocus |
1 | 通常表示+フォーカスあり(デバッグ向き) |
vbMinimizedFocus |
2 | 最小化+フォーカスあり |
vbMaximizedFocus |
3 | 最大化+フォーカスあり |
vbNormalNoFocus |
4 | 通常表示+フォーカスなし |
vbMinimizedNoFocus |
6 | 最小化+フォーカスなし |
ポイント: Shell関数は非同期で動く。メモ帳が起動した瞬間にMsgBoxが表示される(メモ帳を閉じるのを待たない)。
—
コード(応用版1)– WshShell.Runで終了コード取得
外部プログラムの終了を待ち、終了コードを取得する。バッチファイルの実行結果を判定したい場合に使う。
'============================================================
' ■ WshShell.Runで外部プログラムを実行(応用版1)
' → 終了を待つ + 終了コードを取得
'============================================================
Sub RunWithWshShell()
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")
'--- ★書き換えポイント: 実行するコマンド ---
Dim command As String
command = "cmd.exe /c echo Hello, VBA!"
'← /c はコマンド実行後にcmd.exeを閉じるオプション
'← テスト用: echo でメッセージを表示する
' 動作確認したい場合は末尾に「 & pause」を追加するとcmd画面が残る
'--- 実行(同期: 終了まで待つ)
Dim exitCode As Long
exitCode = wsh.Run(command, 1, True)
'← 第2引数 1 = 通常表示
'← 第3引数 True = 終了まで待つ(同期実行)
MsgBox "コマンドが完了しました。" & vbCrLf & _
"終了コード: " & exitCode, vbInformation
Set wsh = Nothing
End Sub
WshShell.Runの引数
| 引数 | 説明 | 値の例 |
|---|---|---|
| 第1引数 | 実行するコマンド文字列 | "cmd.exe /c dir" |
| 第2引数 | ウィンドウスタイル | 0=非表示, 1=通常, 7=最小化 |
| 第3引数 | 終了待ち | True=待つ(同期), False=待たない(非同期) |
—
コード(応用版2)– WshShell.Execで標準出力を取得
コマンドの実行結果(標準出力)をVBAで受け取る。dir や ipconfig などの出力をExcelに取り込みたい場合に使う。
'============================================================
' ■ WshShell.Execで標準出力を取得(応用版2)
' → コマンドの出力をVBAで読み取る
'============================================================
Sub ExecAndReadOutput()
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")
'--- ★書き換えポイント: 実行するコマンド ---
Dim command As String
command = "cmd.exe /c dir C:\ /b"
'← /b はファイル名のみ表示するオプション
'--- 実行
Dim exec As Object
Set exec = wsh.Exec(command)
'--- 標準出力を読み取る
' ※出力が大量(数MB以上)の場合はReadLineでループ読み取りを推奨
Dim output As String
output = exec.StdOut.ReadAll
'--- 結果をイミディエイトウィンドウに出力
Debug.Print "--- コマンド出力 ---"
Debug.Print output
'--- 結果をセルA1に書き込む例
ActiveSheet.Range("A1").Value = output
MsgBox "コマンド出力をA1に書き込みました。" & vbCrLf & _
"終了コード: " & exec.ExitCode, vbInformation
Set exec = Nothing
Set wsh = Nothing
End Sub
ポイント: exec.StdOut.ReadAll でコマンドの全出力を一括取得する。出力が大量(数MB以上)の場合はバッファが詰まる可能性があるため、Do Until exec.StdOut.AtEndOfStream ループで ReadLine を使って1行ずつ読み取る方が安全。
—
コード(実務版)– バッチ実行 + 終了待ち + ログ記録
この方法を覚えてからは、データ前処理のバッチ→Excel集計→ログ記録までをボタン1つで完結させている。朝の15分が浮いた。
以下は、指定したバッチファイルを実行し、終了を待ち、結果をログファイルに記録する実務向けコード。エラー処理も含む。
※ 外部コマンドはファイルの作成・上書き・削除を行う可能性があります。実行前に必ずバックアップを取ってください。
'============================================================
' ■ バッチファイル実行 + 終了待ち + ログ記録(実務版)
' → 指定バッチを実行→完了待ち→ログ記録
' → エラー処理付き
'============================================================
Sub RunBatchWithLog()
'--- ★書き換えポイント ---
Dim batchPath As String
batchPath = "C:\work\daily_report.bat" '← 実行するバッチファイルのパス
Dim logPath As String
logPath = "C:\work\vba_exec_log.txt" '← ログファイルの出力先
' ※ログファイルのフォルダ(C:\work)が存在しない場合はエラーになる
' 事前にフォルダを作成するか、Dir関数で存在チェックを追加
Dim windowStyle As Long
windowStyle = 0 '← 0=非表示, 1=通常表示
'--- ★ここまで ---
'--- バッチファイルの存在チェック
If Dir(batchPath) = "" Then
MsgBox "バッチファイルが見つかりません。" & vbCrLf & _
"パス: " & batchPath, vbExclamation
Exit Sub
End If
'--- ログ記録用のサブルーチン呼び出し(開始ログ)
Call WriteLog(logPath, "開始", batchPath, "")
'--- WshShell.Runで同期実行
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")
Dim exitCode As Long
Dim startTime As Double
startTime = Timer
On Error GoTo ErrHandler
exitCode = wsh.Run("cmd.exe /c """ & batchPath & """", windowStyle, True)
Dim elapsed As Double
elapsed = Timer - startTime
'--- 終了コード判定
Dim resultMsg As String
If exitCode = 0 Then
resultMsg = "正常終了(終了コード: 0)"
Else
resultMsg = "異常終了(終了コード: " & exitCode & ")"
End If
'--- ログ記録(完了ログ)
Call WriteLog(logPath, "完了", batchPath, _
resultMsg & " / 実行時間: " & Format(elapsed, "0.0") & "秒")
MsgBox resultMsg & vbCrLf & _
"実行時間: " & Format(elapsed, "0.0") & "秒" & vbCrLf & _
"ログ: " & logPath, vbInformation
GoTo Cleanup
ErrHandler:
Call WriteLog(logPath, "エラー", batchPath, _
"エラー番号: " & Err.Number & " / " & Err.Description)
MsgBox "実行中にエラーが発生しました。" & vbCrLf & _
"エラー番号: " & Err.Number & vbCrLf & _
Err.Description, vbCritical
Cleanup:
Set wsh = Nothing
End Sub
'============================================================
' ■ ログ書き込みサブルーチン
'============================================================
Private Sub WriteLog(ByVal logPath As String, _
ByVal status As String, _
ByVal target As String, _
ByVal detail As String)
Dim f As Long
f = FreeFile
Open logPath For Append As #f
Print #f, Format(Now, "yyyy/mm/dd hh:nn:ss") & _
" [" & status & "] " & target & _
IIf(detail <> "", " -- " & detail, "")
Close #f
End Sub
書き換えポイント
| 変数 | 説明 | 初期値 |
|---|---|---|
batchPath |
実行するバッチファイルのパス | C:\work\daily_report.bat |
logPath |
ログファイルの出力先 | C:\work\vba_exec_log.txt |
windowStyle |
ウィンドウスタイル | 0(非表示) |
ログの出力例
2026/03/18 09:00:01 [開始] C:\work\daily_report.bat
2026/03/18 09:00:15 [完了] C:\work\daily_report.bat -- 正常終了(終了コード: 0) / 実行時間: 14.2秒
エラー処理の詳細は エラー処理(On Error)で止まらないマクロを作る方法 を参照。ログ記録の詳細は マクロの実行ログをファイルに自動記録する方法 を参照。
—
よくある落とし穴5選
1. Shell関数は非同期 — 処理完了を待たずに次のコードが動く
自分もこれで1時間は悩んだ。Shell関数でバッチファイルを起動したのに、処理が終わる前に次のVBAコードが動いてしまい、まだ生成されていないファイルを開こうとしてエラーになった。「なぜファイルが存在しないのか」と原因を探していたが、原因はShellの非同期実行だった。
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 1 | Shellの直後にファイルを開こうとするとエラー | Shell関数は非同期で次のコードに進む | WshShell.Run(第3引数True)で同期実行に変更 |
2. パスにスペースが含まれるとプログラムが起動しない
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 2 | C:\Program Files\... のパスでエラー |
スペースがパスの区切りと解釈される | パス全体をダブルクォーテーションで囲む |
' NG: スペースで区切られてしまう
Shell "C:\Program Files\MyApp\app.exe", vbNormalFocus
' OK: ダブルクォーテーションで囲む
Shell """C:\Program Files\MyApp\app.exe""", vbNormalFocus
3. Shell関数のウィンドウスタイル引数を忘れる
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 3 | コマンドプロンプトの黒い画面が一瞬チラつく | ウィンドウスタイルが未指定(既定はvbMinimizedFocus) | vbHide(非表示)を指定。開発中はvbNormalFocusでデバッグ |
4. WshShell.Runの同期実行中にExcelが固まる
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 4 | 長時間コマンドの同期実行でExcelが「応答なし」 | VBAは同期実行中に他の処理を受け付けない | 長時間コマンドはShell(非同期)で起動し、完了をループで確認 |
5. cmd.exe /c と /k を間違える
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 5 | コマンドプロンプトが閉じない | /k はコマンド実行後にcmd.exeを終了しない |
バッチ実行には /c を使う(実行後にcmd.exeを閉じる) |
' /c: コマンド実行後にcmd.exeを閉じる(通常はこちら)
Shell "cmd.exe /c C:\work\test.bat", vbNormalFocus
' /k: コマンド実行後もcmd.exeを開いたまま(デバッグ用)
Shell "cmd.exe /k C:\work\test.bat", vbNormalFocus
—
VBAのShell関数で外部プログラムが起動しないときの対処法
「Shell関数を書いたのにプログラムが起動しない」という場合、原因はパスにスペースが含まれていることが多い。C:\Program Files\... のようなパスはダブルクォーテーションで囲む必要がある。パス全体を """C:\Program Files\MyApp\app.exe""" のように記述すれば解決する。
VBAでShell実行後にファイルが見つからないエラーが出るときの対処法
「Shellでバッチを実行した直後にファイルを開こうとするとエラーになる」という場合、原因はShell関数の非同期実行だ。Shell関数は外部プログラムの終了を待たずに次のコードに進むため、ファイルがまだ生成されていない。WshShell.Runの第3引数をTrueにして同期実行に切り替えれば、終了を待ってから次に進める。
FAQ
Q1: Shell関数とWScript.Shellはどう使い分ける?
| 用途 | 推奨 |
|---|---|
| 起動するだけ(結果は不要) | Shell関数 |
| 終了を待ちたい / 終了コードが欲しい | WshShell.Run |
| コマンドの出力を読み取りたい | WshShell.Exec |
自分は「まずShell関数で試して、終了待ちが必要になったらWshShell.Runに切り替える」パターンが多い。
Q2: バッチファイル(.bat)を実行するにはどう書く?
' Shell関数の場合
Shell "cmd.exe /c ""C:\work\daily_report.bat""", vbNormalFocus
' WshShell.Runの場合
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")
wsh.Run "cmd.exe /c ""C:\work\daily_report.bat""", 1, True
Set wsh = Nothing
パスにスペースが含まれる場合は、パスをダブルクォーテーションで囲む。
Q3: コマンドプロンプトのコマンド(dir, copy等)を直接実行できる?
できる。cmd.exe /c コマンド の形式で渡す。
' dirコマンドの実行例
Shell "cmd.exe /c dir C:\work > C:\work\filelist.txt", vbHide
dir や copy はcmd.exeの内部コマンドなので、cmd.exe /c を付けないと実行できない。
Q4: PowerShellのスクリプトを実行できる?
できる。
Shell "powershell.exe -ExecutionPolicy Bypass -File ""C:\work\script.ps1""", vbNormalFocus
-ExecutionPolicy Bypass はスクリプト実行ポリシーの制限を一時的に回避するオプション。組織のセキュリティポリシーに従って使用すること。
Q5: 実行したプログラムの戻り値(終了コード)を取得するには?
WshShell.Runの同期実行時の戻り値が終了コード。Shell関数ではプロセスIDしか取得できない。
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")
Dim exitCode As Long
exitCode = wsh.Run("cmd.exe /c C:\work\test.bat", 0, True)
'← 第3引数True(同期)のときだけ終了コードが返る
If exitCode = 0 Then
MsgBox "正常終了"
Else
MsgBox "異常終了(コード: " & exitCode & ")"
End If
—
まとめ
- Shell関数: VBA標準。非同期で外部プログラムを起動するだけ。参照設定不要
- WshShell.Run: 同期/非同期を選択可能。終了コードを取得できる。バッチ実行の定番
- WshShell.Exec: 標準出力を読み取れる。コマンド結果をExcelに取り込む場合に使う
- cmd.exe /c: バッチファイルや内部コマンド(dir等)の実行時に必須
- パスのスペース: ダブルクォーテーションで囲むことを忘れない
- 安全性: 信頼できないコマンドは絶対に実行しない。テストは無害なコマンドから
関連記事
- ファイルやフォルダの存在を確認してから処理する方法 — 実行前のファイル存在チェック
- エラー処理(On Error)で止まらないマクロを作る方法 — Shell実行時のエラーハンドリング
- マクロの実行ログをファイルに自動記録する方法 — 実行結果のログ記録
- ファイルをZIP圧縮・解凍する方法 — Shell.Applicationを使ったZIP操作
- マクロをボタン1つで実行する方法 — Shell実行マクロをボタンに割り当て
—
次にやりたくなること
- Excelを閉じていてもマクロを定時実行する方法: Shell+タスクスケジューラを組み合わせれば、PCが起動している限り自動で処理を回せる
- ファイルをZIP圧縮・解凍する方法: Shell.Applicationを使えば、VBAだけでファイルのZIP圧縮・解凍ができる
- マクロの実行ログをファイルに自動記録する方法: バッチ実行の結果を日時付きでログファイルに残したい場合に便利
- ファイルやフォルダの存在を確認してから処理する方法: Shell実行前にバッチファイルの存在チェックを入れておくと安全


コメント