【VBA】ShellでExcelから外部プログラム・コマンドを実行する方法(コピペOK)

VBA
スポンサーリンク
スポンサーリンク
  1. この記事でわかること
    1. どんな場面で使う?
  2. 完成イメージ(Before / After)
  3. 実行前の準備
    1. バックアップを取る
    2. Excelをマクロ有効ブック(.xlsm)で保存する
  4. Shell関数とWScript.Shellの使い分け
  5. 手順(コピペ → 実行まで約5分)
    1. VBE(コードを書く画面)を開く
    2. 標準モジュールを挿入する
    3. コードを貼り付けて実行する
  6. コード(最小版)– Shell関数でメモ帳を開く
    1. 書き換えポイント
    2. ウィンドウスタイルの一覧
  7. コード(応用版1)– WshShell.Runで終了コード取得
    1. WshShell.Runの引数
  8. コード(応用版2)– WshShell.Execで標準出力を取得
  9. コード(実務版)– バッチ実行 + 終了待ち + ログ記録
    1. 書き換えポイント
    2. ログの出力例
  10. よくある落とし穴5選
    1. 1. Shell関数は非同期 — 処理完了を待たずに次のコードが動く
    2. 2. パスにスペースが含まれるとプログラムが起動しない
    3. 3. Shell関数のウィンドウスタイル引数を忘れる
    4. 4. WshShell.Runの同期実行中にExcelが固まる
    5. 5. cmd.exe /c と /k を間違える
    6. VBAのShell関数で外部プログラムが起動しないときの対処法
    7. VBAでShell実行後にファイルが見つからないエラーが出るときの対処法
  11. FAQ
    1. Q1: Shell関数とWScript.Shellはどう使い分ける?
    2. Q2: バッチファイル(.bat)を実行するにはどう書く?
    3. Q3: コマンドプロンプトのコマンド(dir, copy等)を直接実行できる?
    4. Q4: PowerShellのスクリプトを実行できる?
    5. Q5: 実行したプログラムの戻り値(終了コード)を取得するには?
  12. まとめ
    1. 関連記事
  13. 次にやりたくなること

この記事でわかること

  • VBAのShell関数で外部プログラム(exe)やバッチファイルを起動できる
  • WScript.Shell(WshShell)で終了待ち・終了コード取得・標準出力の読み取りができる
  • Shell関数とWScript.Shellの使い分けが明確になる

対象: Excel 2016以降 / Microsoft 365、Windows 10/11

どんな場面で使う?

  • 毎朝のバッチファイル実行をExcelのボタン1つで自動化したいとき
  • 外部ツールで前処理した結果をExcelに取り込む一連の流れを組みたいとき
  • PowerShellスクリプトやコマンドをVBAから定期実行したいとき
  • バッチ実行の結果(終了コード)をログに記録して管理したいとき

完成イメージ(Before / After)

Before(手作業):

  1. Excelでレポートを開く
  2. エクスプローラーでバッチファイルをダブルクリック
  3. コマンドプロンプトの完了を待つ
  4. 結果ファイルをExcelで手動で開く

After(VBAで自動化):

  1. Excelのボタンを押す
  2. バッチファイルが自動実行→完了を待つ→結果をExcelに取り込む→ログ記録

ボタン1つで「外部コマンド実行→結果取り込み」まで一気通貫で済む。

自分も以前、毎朝出社してからExcelでレポートを開いて、手動でバッチファイルを実行して、結果をまた手作業でExcelに取り込んでいた。この「Excel→バッチ→Excel」の往復が地味にめんどくさかった。VBAからShellでバッチファイルを直接呼び出せるようにしてからは、ボタン1つで前処理から集計まで一気に終わるようになった。ExcelとコマンドプロンプトやバッチファイルをVBAでつなげると、できることが一気に広がる。この記事で、その第一歩を踏み出してもらえればうれしい。

VBAのShell関数またはWScript.Shellを使えば、Excelからワンクリックで外部プログラムやコマンドを実行できる。

なお、実行するファイルやフォルダの存在を事前にチェックしたい場合は ファイルやフォルダの存在を確認してから処理する方法 を参照。

実行前の準備

バックアップを取る

外部コマンドの実行は、ファイルの作成・上書き・削除など予期しない結果を招く可能性がある。必ずファイルのコピーを別フォルダに保存してから実行する。

注意: Shell関数やWScript.Shellは任意の外部プログラムを実行できる強力な機能です。信頼できないコマンドや不明なバッチファイルは絶対に実行しないでください。テスト時は無害なコマンド(notepad等)から始めてください。

Excelをマクロ有効ブック(.xlsm)で保存する

拡張子が .xlsx のままだとマクロが保存できない。

  1. 「ファイル」→「名前を付けて保存」
  2. ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
  3. 保存

Shell関数とWScript.Shellの使い分け

VBAから外部プログラムを実行する方法は主に2つある。まずはこの比較表で全体像をつかむ。

項目 Shell関数(VBA標準) WshShell.Run WshShell.Exec
参照設定 不要 不要(CreateObject) 不要(CreateObject)
同期/非同期 非同期のみ 選択可能 同期的に出力読み取り
終了コード取得 不可(プロセスIDのみ) 可能(同期時) 可能
標準出力の取得 不可 不可 可能
ウィンドウ制御 vbHide等で制御 0〜10で制御 常に非表示
用途 起動するだけ 終了待ち・終了コード コマンド出力の読み取り

結論: 「起動するだけ」ならShell関数。「終了を待ちたい」「終了コードが欲しい」ならWshShell.Run。「コマンドの出力を読み取りたい」ならWshShell.Exec。

手順(コピペ → 実行まで約5分)

VBE(コードを書く画面)を開く

  1. Excelで Alt + F11 を押す

標準モジュールを挿入する

  1. VBEのメニュー →「挿入」→「標準モジュール」

コードを貼り付けて実行する

  1. コードウィンドウに、下のコードをそのままコピペする
  2. 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で受け取る。diripconfig などの出力を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

dircopy は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等)の実行時に必須
  • パスのスペース: ダブルクォーテーションで囲むことを忘れない
  • 安全性: 信頼できないコマンドは絶対に実行しない。テストは無害なコマンドから

関連記事

次にやりたくなること

コメント

タイトルとURLをコピーしました