🕌
PowerShellでExcelを操作してみよう
Excel-Fun.xls*様の勉強会で登壇しました。
概要
PowerShellとは
WindowsOSに標準搭載されているCLI・スクリプトツール。
Mac/Linuxにもインストールできる。
PowerShellで何ができる?
-
Windows の操作
- ファイル操作(コピー、移動、削除、圧縮)
- レジストリ操作
- タスクスケジューラの設定
- イベントログの取得・解析
- サービスの開始・停止・監視
- プロセスやパフォーマンスの監視
-
アプリケーション制御(特に Office)
- Excel、Word、Outlook などの操作(COM オブジェクト)
- ファイル作成・データ書き込み・印刷・送信など
- Active Directory の操作(ユーザー・グループ管理)
- SQL Server などのデータベース操作(ODBC / ADO.NET)
-
ネットワーク・システム管理
- ネットワーク接続確認(Test-Connection / Test-NetConnection)
- リモートPCの操作(PowerShell Remoting)
- IP設定やFirewallの構成変更
- DNS / DHCP サーバーの管理(モジュールあり)
-
スクリプトやツールの作成
COM(Component Object Model)とは?
-
コンポーネント化
ソフトウェアをコンポーネント(部品)化し、それを他のアプリケーションで利用できるようにする。 -
言語非依存
異なる言語で書かれたコンポーネントが相互に通信できるようにする。 -
プロセス間通信
異なるプロセス間でのオブジェクトのやりとりできるようにする
VBAよりPowerShellの方がいい場面
基本的にVBAとPowerShell(COMオブジェクト)は同じ操作ができますが
PowerShellがいい場面は下記のとおりです。
- 複数のファイルを一括処理したい場合
- Excel以外の処理と連携させたい場合
- GUIを必要としないバックグラウンド処理
- 管理者視点での一括レポート生成やログ集計
操作してみよう
起動

- powershellで検索する
- Windows PowerShellをクリックする
ISE(統合開発環境)は開発が中止されていて、PowerShell+VSCodeが推奨されている
コードを実行する
sample.ps1
# Excel COMオブジェクト作成
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
# 新しいブック作成
$book = $excel.Workbooks.Add()
# シート取得
$sheet = $book.Sheets.Item(1)
# セルに書き込み
$sheet.Cells.Item(1,1).Value = "Hello, Excel!"
コードを実行する2
sample2.ps1
$book.Close($false)
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
- $excelオブジェクトだけ開放してもプロセスが残る
コードを実行する3
sample3.ps1
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($sheet) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($book) | Out-Null
- 宣言したすべてのオブジェクトを開放する
ユーザーが宣言したComオブジェクトを格納したすべての変数に対して解放処理を実施する
garbageCollection.ps1
# COMオブジェクトのみを取得
$variables = Get-Variable | Where-Object {
$_.Value -is [System.__ComObject] -and $_.Name -notmatch '^env:|^global:|^function:'
}
Write-Host "リリース予定: $($var.Name)"
foreach ($var in $variables) {
Write-Host "$($var.Name)"
}
# ユーザーに確認のプロンプトを表示
$confirmation = Read-Host "上記変数をリリースしますか? (yes でリリース)"
# 2回目のループ:リリース処理
if ($confirmation -eq "yes") {
foreach ($var in $variables) {
Write-Host "リリース中: $($var.Name)"
# COMオブジェクトの解放
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($var.Value) | Out-Null
# 変数の参照をnullに設定
$var.Value = $null
}
# ガーベジコレクションを強制実行
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
Write-Host "COMオブジェクトの解放が完了しました"
}
サンプルコード
変数の宣言
- $を先頭につける
variable.ps1
PS C:\> num = 1
console
num : 用語 'num' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されません。
名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認してから、再試行してください
。
発生場所 行:1 文字:1
+ num = 1
+ ~~~
+ CategoryInfo : ObjectNotFound: (num:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
- $を付けないとコマンドや関数と認識される
ファイル・ディレクトリ操作
1
Get-ChildItem.ps1
Get-ChildItem
console
PS C:\Users\towamz\public\Excel-Fun> Get-ChildItem
ディレクトリ: C:\Users\towamz\public\Excel-Fun
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2025/07/16 17:06 【VBA】007_月次請求書を作成_他の人の作品
-a---- 2025/05/09 22:55 28112 【VBA】001_スケジュールへの着色.xlsm
-a---- 2025/06/02 12:14 22579 【VBA】002_カラー定数一覧.xlsx
-a---- 2025/06/01 14:44 23134 【VBA】003_領収書をPDF出力.xlsm
-a---- 2025/06/12 3:08 94565 【VBA】004 カレンダー入力フォームを作ってみよう.xlsm
-a---- 2025/03/24 13:22 577417 【VBA】005_プラモの部品構成表.xlsm
-a---- 2025/04/07 11:24 62747 【VBA】007_月次請求書を作成.xlsm
-a---- 2025/05/04 21:56 752655 【VBA】008_社員名簿.csv
-a---- 2025/05/31 22:34 217963 【VBA】009_人事データからクロス表を作成.xlsm
- オプションなしでカレントディレクトリのファイル・サブディレクトリ一覧が表示される
2
Get-ChildItem.ps1
Get-ChildItem -File
console
PS C:\Users\towamz\public\Excel-Fun> Get-ChildItem -File
ディレクトリ: C:\Users\towamz\public\Excel-Fun
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2025/05/09 22:55 28112 【VBA】001_スケジュールへの着色.xlsm
-a---- 2025/06/02 12:14 22579 【VBA】002_カラー定数一覧.xlsx
-a---- 2025/06/01 14:44 23134 【VBA】003_領収書をPDF出力.xlsm
-a---- 2025/06/12 3:08 94565 【VBA】004 カレンダー入力フォームを作ってみよう.xlsm
-a---- 2025/03/24 13:22 577417 【VBA】005_プラモの部品構成表.xlsm
-a---- 2025/04/07 11:24 62747 【VBA】007_月次請求書を作成.xlsm
-a---- 2025/05/04 21:56 752655 【VBA】008_社員名簿.csv
-a---- 2025/05/31 22:34 217963 【VBA】009_人事データからクロス表を作成.xlsm
- Fileオプションでファイルのみ表示できる
3
Get-ChildItem.ps1
Get-ChildItem -File | ForEach-Object {
$date = Get-Date -Format "yyyyMMdd"
Write-Host ($_.BaseName + "_" + $date + $_.Extension)
}
console
PS C:\Users\towamz\public\Excel-Fun> Get-ChildItem -File | ForEach-Object {
>> $date = Get-Date -Format "yyyyMMdd"
>> Write-Host ($_.BaseName + "_" + $date + $_.Extension)
>> }
【VBA】001_スケジュールへの着色_20250717.xlsm
【VBA】002_カラー定数一覧_20250717.xlsx
【VBA】003_領収書をPDF出力_20250717.xlsm
【VBA】004 カレンダー入力フォームを作ってみよう_20250717.xlsm
【VBA】005_プラモの部品構成表_20250717.xlsm
【VBA】007_月次請求書を作成_20250717.xlsm
【VBA】008_社員名簿_20250717.csv
【VBA】009_人事データからクロス表を作成_20250717.xlsm
- [$_]でパイプで受け取ったオブジェクトを示す
- このコードではFileオブジェクトを受け取っているので、ファイルパス・ファイル名・拡張子などのプロパティにアクセスできる
4
Get-ChildItem.ps1
# ExcelアプリケーションのCOMオブジェクトを作成
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true # Excelを表示(非表示にしたい場合は $false)
# 警告メッセージやプロンプトを非表示にする
$excel.DisplayAlerts = $false
Get-ChildItem -Recurse -Depth 2 -Filter *.xls* -File | ForEach-Object {
Write-Host $_.Name
$book = $excel.Workbooks.Open($_.FullName)
Start-Sleep -Seconds 2
$book.Close($false)
}
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($book) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
console
【VBA】001_スケジュールへの着色.xlsm
【VBA】002_カラー定数一覧.xlsx
【VBA】003_領収書をPDF出力.xlsm
【VBA】004 カレンダー入力フォームを作ってみよう.xlsm
【VBA】005_プラモの部品構成表.xlsm
【VBA】007_月次請求書を作成.xlsm
【VBA】009_人事データからクロス表を作成.xlsm
Kou_【VBA】007_月次請求書を作成.xlsm
【sele_chan】007_月次請求書を作成.xlsm
【いおり】007_月次請求書を作成.xlsm
【いおり】007_月次請求書を作成_ACE版.xlsm
【しゃあ】007_月次請求書を作成.xlsm
【ひろ】007_月次請求書を作成.xlsm
ぷりずむ_【VBA】007_月次請求書を作成.xlsm
ゅぇ。【VBA】007_月次請求書を作成_v1.xlsm
和風スパ【VBA】007_月次請求書を作成.xlsm
- Recurseオプションでサブフォルダも再帰的に処理できる
- Depthオプションでどの階層まで処理するか指定できる
5
Get-ChildItem.ps1
# ExcelアプリケーションのCOMオブジェクトを作成
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true # Excelを表示(非表示にしたい場合は $false)
Get-ChildItem -Recurse -Depth 2 -File |
Where-Object {$_.Name -match "^[^【]"} |
ForEach-Object {
Write-Host $_.Name
$book = $excel.Workbooks.Open($_.FullName)
Start-Sleep -Seconds 2
$book.Close($false)
}
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($book) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
console
Kou_【VBA】007_月次請求書を作成.xlsm
ぷりずむ_【VBA】007_月次請求書を作成.xlsm
ゅぇ。【VBA】007_月次請求書を作成_v1.xlsm
和風スパ【VBA】007_月次請求書を作成.xlsm
- 正規表現でフィルタしたい場合はWhere-Objectを使う
ファイル
UTF-8(BOMあり)かshift-jisで保存する
- UTF-8(BOMなし)は文字化けする
スクリプトファイル(ps1)
既定値はスクリプトファイル実行不可のためセキュリティポリシーを指定する必要がある
セキュリティポリシーを変更しないで実行する
ExecutionPolicy
powershell -ExecutionPolicy Bypass -File .\msgbox.ps1
- 別プロセスで実行する
セキュリティポリシーを変更する
ExecutionPolicy
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
| Scope | 意味 | 有効期限 |
|---|---|---|
| Process | 現在の PowerShell セッション(プロセス)にのみ適用される。 | 該当プロンプトのみ |
| CurrentUser | 現在ログインしているユーザーに対して設定が適用される。 | 該当ユーザーのみ永続的 |
| LocalMachine | システム全体に対して設定が適用され、全てのユーザーに影響を与える。 | すべてのユーザーで永続的 |
| ExecutionPolicy | 説明 |
|---|---|
| Restricted | すべてのスクリプトの実行を禁止(既定の設定) |
| AllSigned | すべてのスクリプトと構成ファイルに、信頼された発行元の署名が必要 |
| RemoteSigned | リモート(インターネットなど)から取得したスクリプトに署名が必要 |
| Unrestricted | すべてのスクリプトを実行可能。初回実行時に警告が表示される |
| Bypass | 実行制限を完全に無視。警告も署名も不要 |
| Undefined | 実行ポリシーを未定義に戻す(親スコープの設定に従う) |
| Default | PowerShell 7 以降で使用可能。OSやグループポリシーの既定設定に従う |
モジュールファイル(psm1)
- 拡張子psm1は、再利用可能な汎用関数を保存するための拡張子
- クラスは保存できないようです
garbageCollection.psm1
function garbageCollection(){
# COMオブジェクトのみを取得
$variables = Get-Variable | Where-Object {
$_.Value -is [System.__ComObject] -and $_.Name -notmatch '^env:|^global:|^function:'
}
Write-Host "リリース予定: $($var.Name)"
foreach ($var in $variables) {
Write-Host "$($var.Name)"
}
# ユーザーに確認のプロンプトを表示
$confirmation = Read-Host "上記変数をリリースしますか? (yes でリリース)"
# 2回目のループ:リリース処理
if ($confirmation -eq "yes") {
foreach ($var in $variables) {
Write-Host "リリース中: $($var.Name)"
# COMオブジェクトの解放
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($var.Value) | Out-Null
# 変数の参照をnullに設定
$var.Value = $null
}
# ガーベジコレクションを強制実行
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
Write-Host "COMオブジェクトの解放が完了しました"
}
}
# モジュール内でエクスポートする関数を指定
Export-ModuleMember -Function garbageCollection
- Export-ModuleMemberでどの関数を利用可能にするかを指定することができる
- 指定しなければすべての関数が利用可能になる
main.ps1
Import-Module .\GarbageCollection.psm1
タスクスケジューラ
taskscheduler.ps1
$ScriptPath = "C:\Users\towamz\public\ps\0_task\myfirsttask.ps1"
$Taskname = "myfirsttask"
# $time = Get-Date "2025-07-20 21:30"
$time = (Get-Date).Date.AddHours(21).AddMinutes(45)
$Action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-File `"$ScriptPath`""
$Trigger = New-ScheduledTaskTrigger -Once -At $time
$Principal = New-ScheduledTaskPrincipal -UserId "towamz" -LogonType Interactive
# $Principal = New-ScheduledTaskPrincipal -UserId "$env:USERNAME" -LogonType Interactive
# $Principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Unregister-ScheduledTask -TaskName $Taskname -Confirm:$false
Register-ScheduledTask -TaskName $Taskname -Action $Action -Trigger $Trigger -Principal $Principal
Get-ScheduledTask -TaskName $Taskname | Get-ScheduledTaskInfo
| triggerオプション | 説明 | 例 |
|---|---|---|
| -AtStartup | PC起動時に実行 | |
| -AtLogOn | 任意のユーザーがログオンしたときに実行 | |
| -AtLogOn -User "<ユーザー名>" | 特定のユーザーがログオンしたときに実行 | |
| -Daily -At "HH:mm" | 毎日指定した時刻に実行 | -Daily -At "09:00" |
| -Weekly -DaysOfWeek <曜日> -At "HH:mm" | 毎週指定した曜日に実行 | -Weekly -DaysOfWeek Monday -At "08:00" |
| -Monthly -DaysOfMonth <日> -At "HH:mm" | 毎月の特定日に実行 | Monthly -DaysOfMonth 1,15 -At "07:00" |
| -Once -At "HH:mm" | 指定した時刻に1回だけ実行 | -Once -At "14:30" |
| LogonTypeオプション | 説明 |
|---|---|
| Interactive | ユーザーがログオンしているときにのみ実行。GUI操作や画面表示が可能 |
| Password | ユーザー名とパスワードで認証し実行。バックグラウンドで動作 |
| ServiceAccount | SYSTEMやNETWORK SERVICEなどのサービスアカウントで実行 |
Discussion