株式会社Good Labでエンジニアをしている コータロー です。
日々、Java・SQL・Gitなどの技術情報や、新人エンジニア向けの学習ノウハウ、
AI活用についての情報を発信しています。
Good Labについて気になった方は、コーポレートサイトもぜひご覧ください。
▶コーポレートサイト
本記事は 「PowerShell 業務自動化レシピ」 シリーズの第5回です。このシリーズは、文法解説ではなく 「1記事=1つの業務課題」を、コピペで動く検証済みスクリプトで解決することを目的とした実務レシピ集です(全12回想定)。SES・受託・社内運用の現場で、いまだに手作業でやっている地味な作業を片っ端から自動化していきます。
第1回 では「大量ファイルの一括リネーム」、第2回 では「古いログファイルの抽出・自動削除」、第3回 では「フォルダ構成・ファイル一覧をExcel台帳化」、第4回 では「複数CSVを1枚に結合して集計」を扱いました。第3回の最後で ImportExcel モジュールを使って .xlsx を直接出力する例にちらっと触れましたが、今回はその本格活用編です。
第5回のテーマは 「Excelを開かずにセル値を読み書きする」。ImportExcel モジュールを使い、.xlsx のセルを Excel本体を起動せずに直接読み書きします。「毎月決まったExcelテンプレに集計値を流し込んで提出」「複数のExcelブックから合計欄だけ抜き出して一覧化」——Excelを1つずつ開いて転記していた作業を、コマンド一本でバッチ処理します。
本記事も基準は PowerShell 7(pwsh) ですが、今回の主役 ImportExcel は Windows PowerShell 5.1 でも動作します(クロスプラットフォーム対応)。これまでの回で扱った「5.1ではエンコード指定が違う」という話とは性質が異なり、ImportExcel のコマンド自体は 5.1 と 7 で共通です。この点は最後の章で改めて整理します。
⚠️ 今回は 既存のExcelファイルを書き換える(上書き保存する)レシピを含みます。第3〜4回と違い破壊的な操作になり得るので、テンプレや元ブックは事前にバックアップを取ってから試してください(安全策は後述)。
事前準備:ImportExcel のインストール
ImportExcel は PowerShell標準ではないサードパーティ製モジュールです。初回だけ、PowerShell Gallery からインストールします。
# 現在のユーザー範囲にインストール(管理者権限不要)
Install-Module ImportExcel -Scope CurrentUser
- 初回は 「信頼されていないリポジトリ」 の確認が出ることがあります。PowerShell Gallery(
PSGallery)からの取得である旨を確認してY(はい)で進めます。 - 実行ポリシーで止まる場合は、現在のユーザーに対してスクリプト実行を許可します(恒久的に緩めたくなければ、そのセッションだけ
Processスコープで一時許可するのが安全です)。
# そのPowerShellセッションの間だけ実行を許可(再起動で元に戻る)
Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned
インストール済みかどうかは次で確認できます。
Get-Module ImportExcel -ListAvailable | Select-Object Name, Version
配布先の環境に ImportExcel が無いことがあります。自分のPCでは動いても、共有サーバーやお客様環境では未導入というのは普通に起きます。納品物にスクリプトを含めるなら、「初回
Install-Moduleが必要」である旨を手順書に明記しておきましょう。
以降のサンプルは、毎回モジュールを読み込む書き方にはしていません(ImportExcel は初回コマンド実行時に自動ロードされます)。明示的に読み込みたい場合は先頭に Import-Module ImportExcel を足してください。
Before:手作業のつらいシーン
シーン1:30店舗のExcelを開いてB2の売上だけ転記する
「各店舗から送られてきた売上報告Excel、合計額(B2セル)だけ抜き出して一覧にして」。売上_新宿店.xlsx 売上_渋谷店.xlsx … が30個。1つずつダブルクリックでExcelを開き、B2セルの数字を見て、集計用ブックに転記。閉じる。次を開く……。
- 1ファイルあたり「開く→該当セル確認→転記→閉じる」で 20〜30秒
- 30店舗なら体感 15〜20分、しかもExcelの起動待ちと転記ミスつき
- 来月もまた同じ作業が発生する(毎月のルーティン)
シーン2:毎月のテンプレExcelに集計値を手入力する
「月次報告テンプレ(月次報告_テンプレート.xlsx)の決まったセルに、今月の集計値を入れて提出して」。テンプレの B2 に売上合計、B3 に件数、B4 に平均単価……と、毎月同じセルに手入力。値の出どころは別の集計結果なのに、目視で見ながら手打ちで転記する。
- セル位置を間違える・桁を打ち間違える事故が起きる
- 「先月のテンプレを使い回したら古い値が残っていた」もよくある
どちらも、Excelを一度も開かずに、PowerShellからセルを直接読み書きすれば一瞬で終わります。ImportExcel の出番です。
After:スクリプト一本で完了
進め方は ①ブックを丸ごとオブジェクト配列として読む〔Import-Excel〕→ ②特定セルだけピンポイントで読む/書く〔Open-ExcelPackage〕→ ③複数ブックから同じセルを集めて一覧化 → ④テンプレに集計値を流し込み+整形出力〔Export-Excel〕 の順です。
この記事で使うサンプルExcelの中身を、先に固めておきます(各自で同じ内容のブックを作れば、出力例がそのまま再現できます)。
売上_新宿店.xlsx(シート名 Sheet1)
| A | B | |
|---|---|---|
| 1 | 店舗名 | 新宿店 |
| 2 | 売上合計 | 1200000 |
| 3 | 客数 | 480 |
売上_渋谷店.xlsx(シート名 Sheet1)
| A | B | |
|---|---|---|
| 1 | 店舗名 | 渋谷店 |
| 2 | 売上合計 | 1500000 |
| 3 | 客数 | 600 |
売上_池袋店.xlsx(シート名 Sheet1)
| A | B | |
|---|---|---|
| 1 | 店舗名 | 池袋店 |
| 2 | 売上合計 | 900000 |
| 3 | 客数 | 360 |
3ブックとも「A列がラベル、B列が値」という同じレイアウトで、
C:\report(macOS/Linuxなら~/report)に置いてある想定です。B1=店舗名、B2=売上合計、B3=客数 というセル配置を、以降のレシピで読み書きします。
レシピ1:ブックを丸ごとオブジェクト配列として読む(Import-Excel)
「1行目をヘッダーとして、表データをまるごと読み込む」用途には Import-Excel が最速です。CSVを Import-Csv で読むのと同じ感覚で、.xlsx をそのままオブジェクトの配列にできます。
ここでは横並びのサンプルとは別に、表形式のブック 集計表.xlsx(シート Data)を想定します。
集計表.xlsx(シート名 Data、1行目がヘッダー)
| A | B | C | |
|---|---|---|---|
| 1 | 店舗 | 売上 | 客数 |
| 2 | 新宿店 | 1200000 | 480 |
| 3 | 渋谷店 | 1500000 | 600 |
| 4 | 池袋店 | 900000 | 360 |
# ===== 設定 =====
$xlsx = "C:\report\集計表.xlsx"
# ===== 本体 =====
# 1行目(店舗 / 売上 / 客数)が自動でプロパティ名になる
$rows = Import-Excel -Path $xlsx -WorksheetName 'Data'
$rows | Format-Table -AutoSize
出力イメージ(ヘッダー行が列名になり、各行がオブジェクトになります):
店舗 売上 客数
---- ---- ----
新宿店 1200000 480
渋谷店 1500000 600
池袋店 900000 360
Import-Csv のときと同じく、ここで読み込んだ値も基本は文字列扱いになります(第4回の最重要ポイントと同じ落とし穴です)。合計するときは数値へキャストします。
# 売上の合計([double] で数値化してから Measure-Object)
$sum = ($rows | Measure-Object { [double]$_.売上 } -Sum).Sum
Write-Host "売上合計: $sum" # => 売上合計: 3600000
検算:1200000 + 1500000 + 900000 = 3600000。3行の合計と一致します。
レシピ2:特定セルだけをピンポイントで読む/書く(Open-ExcelPackage)
シーン1・2の本丸はこちらです。「B2セルの値だけ欲しい」「B2セルに値を書きたい」という、表ではなくセル単位の操作には Open-ExcelPackage を使います。Excelファイルを「パッケージ」として開き、$sheet.Cells["B2"].Value でセルに直接アクセスできます。
読む(B2の売上合計を取得):
# ===== 設定 =====
$xlsx = "C:\report\売上_新宿店.xlsx"
# ===== 本体 =====
$pkg = Open-ExcelPackage -Path $xlsx # ブックをパッケージとして開く
$sheet = $pkg.Workbook.Worksheets['Sheet1'] # シート名で取得([0] でもOK)
$store = $sheet.Cells['B1'].Value # 店舗名
$sales = $sheet.Cells['B2'].Value # 売上合計
$guest = $sheet.Cells['B3'].Value # 客数
Write-Host "$store の売上合計は $sales 円(客数 $guest)"
# 読むだけなので保存せずに閉じる(-Save を付けない)
Close-ExcelPackage $pkg -NoSave
出力:
新宿店 の売上合計は 1200000 円(客数 480)
書く(B2の値を書き換えて保存):
# ===== 設定 =====
$xlsx = "C:\report\売上_新宿店.xlsx"
# ===== 本体 =====
$pkg = Open-ExcelPackage -Path $xlsx
$sheet = $pkg.Workbook.Worksheets['Sheet1']
# B2 を 1,250,000 に更新(数値として書き込む)
$sheet.Cells['B2'].Value = 1250000
# B4 にラベルと値を新規に書き足す
$sheet.Cells['A4'].Value = '更新日'
$sheet.Cells['B4'].Value = (Get-Date -Format 'yyyy/MM/dd')
# ★保存する場合は -Save を必ず付ける(付け忘れると変更が消える)
Close-ExcelPackage $pkg -Save
このスクリプトを実行すると 売上_新宿店.xlsx の中身はこうなります(Excelを一度も開かずに更新されています)。
| A | B | |
|---|---|---|
| 1 | 店舗名 | 新宿店 |
| 2 | 売上合計 | 1250000 |
| 3 | 客数 | 480 |
| 4 | 更新日 | 2026/06/30 |
最大のハマりどころは保存し忘れです。
Open-ExcelPackageで開いて値を書き換えても、Close-ExcelPackageに-Saveを付けないと変更は破棄されます。逆に、読むだけのときに誤って-Saveすると無駄に更新日時が変わるので、読み取りは-NoSave、書き込みは-Saveと意識して使い分けます。
⚠️ 書き込み系は元ファイルを直接上書きします。テンプレや原本を壊さないよう、実行前に
Copy-Itemでバックアップを取るか、後述のようにコピーを作ってからそのコピーに書き込む運用にしてください。
レシピ3:複数ブックから同じセルを集めて一覧化する(シーン1の解決)
C:\report にある 売上_*.xlsx を全部開き、各ブックの B1(店舗名)・B2(売上合計)・B3(客数) を抜き出して1枚の一覧にします。シーン1(30店舗を手で転記)をこれ1本で置き換えます。
# ===== 設定 =====
$targetDir = "C:\report"
$pattern = "売上_*.xlsx"
$outXlsx = "C:\report\_店舗別サマリ.xlsx"
# ===== 本体 =====
$summary = Get-ChildItem -LiteralPath $targetDir -File -Filter $pattern |
ForEach-Object {
$pkg = Open-ExcelPackage -Path $_.FullName
$sheet = $pkg.Workbook.Worksheets['Sheet1']
# B1/B2/B3 をピンポイントで読む
$obj = [pscustomobject]@{
ファイル名 = $_.Name
店舗 = $sheet.Cells['B1'].Value
売上 = [int]$sheet.Cells['B2'].Value
客数 = [int]$sheet.Cells['B3'].Value
}
# 読むだけなので保存せず閉じる
Close-ExcelPackage $pkg -NoSave
$obj
}
# 画面で確認
$summary | Sort-Object 売上 -Descending | Format-Table -AutoSize
# xlsx に一覧出力(見出し固定+オートフィルタ付き)
$summary |
Sort-Object 売上 -Descending |
Export-Excel -Path $outXlsx -WorksheetName 'サマリ' -AutoSize -FreezeTopRow -TableName 'Summary'
Write-Host "一覧を出力しました: $outXlsx"
画面出力イメージ(売上の多い順):
ファイル名 店舗 売上 客数
---------- ---- ---- ----
売上_渋谷店.xlsx 渋谷店 1500000 600
売上_新宿店.xlsx 新宿店 1200000 480
売上_池袋店.xlsx 池袋店 900000 360
売上_新宿店.xlsxはレシピ2で B2 を 1250000 に書き換えていない、元の状態を前提にした出力例です(レシピ2を実行済みなら新宿店は 1250000 になります)。レシピ間で同じファイルを使い回すときは、書き込み系を試した後に読み取り系の値が変わる点に注意してください。
これで「30店舗のExcelを1つずつ開いてB2を転記」が、フォルダにファイルを集めてコマンド一本、になります。Open-ExcelPackage でセルを直接読むので、Excelの起動待ちが一切ありません。
レシピ4:テンプレExcelに集計値を流し込んで整形出力する(シーン2の解決)
最後はシーン2。毎月のテンプレに今月の集計値を書き込みます。ここでは2つの方法を示します。
(a) 既存テンプレの決まったセルに値を流し込む(Open-ExcelPackage)
テンプレ 月次報告_テンプレート.xlsx(シート 報告)が、こういうレイアウトだとします。
月次報告_テンプレート.xlsx(シート名 報告)
| A | B | |
|---|---|---|
| 1 | 項目 | 値 |
| 2 | 売上合計 | (空欄) |
| 3 | 来店客数 | (空欄) |
| 4 | 平均客単価 | (空欄) |
レシピ3で集めた $summary から合計を出して、テンプレの B2〜B4 に書き込みます。原本を壊さないよう、まずテンプレを当月用にコピーしてから、そのコピーに書き込みます。
# ===== 設定 =====
$template = "C:\report\月次報告_テンプレート.xlsx"
$stamp = Get-Date -Format "yyyyMM"
$outXlsx = "C:\report\月次報告_$stamp.xlsx" # 例: 月次報告_202606.xlsx
# ===== 本体 =====
# 1) テンプレを当月ファイルとしてコピー(原本は触らない)
Copy-Item -LiteralPath $template -Destination $outXlsx -Force
# 2) レシピ3の要領で各店舗の値を集める(ここでは $summary が手元にある前提)
$totalSales = ($summary | Measure-Object 売上 -Sum).Sum
$totalGuest = ($summary | Measure-Object 客数 -Sum).Sum
$avgSpend = [math]::Round($totalSales / $totalGuest, 0)
# 3) コピーしたファイルを開いて、決まったセルに流し込む
$pkg = Open-ExcelPackage -Path $outXlsx
$sheet = $pkg.Workbook.Worksheets['報告']
$sheet.Cells['B2'].Value = $totalSales
$sheet.Cells['B3'].Value = $totalGuest
$sheet.Cells['B4'].Value = $avgSpend
Close-ExcelPackage $pkg -Save # ★保存を忘れない
Write-Host "$outXlsx に流し込みました(売上 $totalSales / 客数 $totalGuest / 客単価 $avgSpend)"
$summary(新宿1200000/480・渋谷1500000/600・池袋900000/360)から計算すると、出力ファイル 月次報告_202606.xlsx の中身はこうなります。
| A | B | |
|---|---|---|
| 1 | 項目 | 値 |
| 2 | 売上合計 | 3600000 |
| 3 | 来店客数 | 1440 |
| 4 | 平均客単価 | 2500 |
検算:売上合計 1200000+1500000+900000=3600000、来店客数 480+600+360=1440、平均客単価 3600000 / 1440 = 2500。Write-Host の出力は次のとおりです。
C:\report\月次報告_202606.xlsx に流し込みました(売上 3600000 / 客数 1440 / 客単価 2500)
(b) テンプレが無く、表をまるごと整形して出力したい(Export-Excel)
「決まったテンプレは無いが、集計表を見栄えよく .xlsx で出したい」なら Export-Excel 一発です。第3回で台帳出力に使ったのと同じコマンドで、**見出し固定・列幅自動・テーブル化(オートフィルタ)**まで付きます。
# ===== 設定 =====
$outXlsx = "C:\report\_月次サマリ.xlsx"
# ===== 本体 =====
# $summary(レシピ3)に「客単価」列を足して出力
$summary |
Select-Object `
店舗,
売上,
客数,
@{Name='客単価'; Expression={ [math]::Round($_.売上 / $_.客数, 0) }} |
Sort-Object 売上 -Descending |
Export-Excel -Path $outXlsx -WorksheetName 'サマリ' -AutoSize -FreezeTopRow -TableName 'MonthlySummary'
Write-Host "整形出力しました: $outXlsx"
出力される _月次サマリ.xlsx(シート サマリ、1行目見出し固定+テーブル化):
| 店舗 | 売上 | 客数 | 客単価 |
|---|---|---|---|
| 渋谷店 | 1500000 | 600 | 2500 |
| 新宿店 | 1200000 | 480 | 2500 |
| 池袋店 | 900000 | 360 | 2500 |
客単価はいずれも
売上 / 客数 = 2500です(3店舗とも単価が揃ったサンプルにしてあります)。実データなら店舗ごとに違う値になります。
コードの要点解説(なぜこの書き方か)
文法そのものではなく、現場でハマらないための選択を中心に解説します。
1. 「表として読む」なら Import-Excel、「セル単位」なら Open-ExcelPackage
ImportExcel には読み方が2系統あります。用途で使い分けます。
-
Import-Excel… 1行目をヘッダーとみなし、表をオブジェクト配列にする。Import-CsvのExcel版。表データの一括読み込み・集計向き。 -
Open-ExcelPackage… ブックをパッケージとして開き、$sheet.Cells["B2"]で任意のセルに直接アクセスする。特定セルだけ読む・書くのに使う(書き込みはこちらだけ)。
「合計欄(B2)だけ欲しい」のように位置で値を指す業務は Open-ExcelPackage 一択です。逆に「表全体を集計したい」なら Import-Excel が圧倒的にラクです。
2. セルは A1記法(Cells["B2"])でも 行列番号(Cells[2,2])でも指せる
$sheet.Cells["B2"] のように A1記法(列アルファベット+行番号) で直感的に指せます。ループで回したいときは Cells[行, 列] の数値インデックスも使えます(行・列とも1始まり。Cells[2,2] が B2)。
# 2行目〜4行目の B列(=2列目)を順に読む例
foreach ($r in 2..4) {
$v = $sheet.Cells[$r, 2].Value
Write-Host "B$r = $v"
}
A1記法は人が読み書きする固定セル向き、数値インデックスは範囲をループ処理するときに便利、と覚えておくと迷いません。
3. .Value と .Text と .Formula は別物
セルには複数のプロパティがあり、取りたいものによって使い分けます。
-
.Value… セルの生の値(数値ならdouble、文字列ならstring、日付ならDateTime)。集計に使うのはこれ。 -
.Text… Excelの表示文字列(書式適用後)。1200000が"1,200,000"と桁区切り表示されているなら、.Textは"1,200,000"を返す。見た目どおりの文字列が欲しいとき用。 -
.Formula… セルに入っている数式そのもの(例:"SUM(B2:B4)")。値ではなく式が欲しいとき用。
数式セルの「計算結果」が欲しいなら基本は .Value を読みます。ただしスクリプトで値を書き換えた後の数式の再計算は、Excelエンジンが動かない都合で自動では走りません。再計算させたい場合は Close-ExcelPackage に -Calculate を付けます。
# 数式の再計算をしてから保存したいとき
Close-ExcelPackage $pkg -Save -Calculate
4. 【最重要】書き込んだら Close-ExcelPackage -Save、読むだけなら -NoSave
Open-ExcelPackage で開いたパッケージは、Close-ExcelPackage で必ず閉じます。ここのオプションが肝心です。
-
-Save… 変更を元ファイルに保存して閉じる(書き込み系で必須)。 -
-NoSave… 保存せずに閉じる(読み取り専用のとき。誤って更新日時を変えないため)。
-Save を付け忘れると、せっかく書いたセルの変更がまるごと破棄されます。これが ImportExcel 初心者の一番多い「あれ、書いたのに反映されてない」の正体です。「開いたら閉じる、書いたら -Save」をセットで体に入れてください。
5. Import-Excel の値も「文字列寄り」。集計前にキャスト
第4回の Import-Csv と同様、Import-Excel で読んだ値もそのまま合計すると意図しない結果になりがちです。Measure-Object { [double]$_.売上 } -Sum のように数値へキャストしてから集計します。一方 Open-ExcelPackage の .Value は型を保ったまま返る(数値セルなら数値)ので、レシピ3では [int]$sheet.Cells['B2'].Value と保険のキャストを入れつつ、そのまま Measure-Object 売上 -Sum で集計できています。
6. Export-Excel の整形オプションは第3回と共通
-AutoSize(列幅自動)・-FreezeTopRow(見出し行固定)・-TableName(テーブル化=オートフィルタ付与)は第3回の台帳出力と同じです。-WorksheetName でシート名、-StartRow / -StartColumn で書き出し開始位置も指定できます。CSVと違い書式付きの .xlsx がそのまま手に入るのが ImportExcel の強みです。
応用・注意点
① ImportExcel はサードパーティ製。配布先に無い前提で
冒頭で触れたとおり、ImportExcel は PowerShell標準ではないため、Install-Module ImportExcel -Scope CurrentUser が済んでいない環境では動きません。お客様環境・共有サーバーで動かす想定なら、手順書に導入手順を書くか、導入済みか事前に確認します。
# 未導入なら自動でインストールする防御的な書き方
if (-not (Get-Module ImportExcel -ListAvailable)) {
Install-Module ImportExcel -Scope CurrentUser -Force
}
Import-Module ImportExcel
インターネットに出られない閉域環境では
Install-Moduleが失敗します。その場合は別PCでSave-Module ImportExcel -Path <フォルダ>してモジュール一式を持ち込む、という方法があります。
② ファイルがExcelで開かれていると書き込みでロックされる
対象の .xlsx を自分や誰かがExcelで開いたままだと、Open-ExcelPackage での書き込み保存がファイルロックで失敗します(読み取りも失敗することがあります)。
- バッチ実行の前に、対象ブックがExcelで開かれていないことを確認する
- 共有フォルダ上のファイルは、他人が開いている可能性も考慮する
- ロックで失敗したら
Close-ExcelPackageが走らずハンドルが残ることがあるため、try/finallyで確実に閉じる書き方が安全です
$pkg = Open-ExcelPackage -Path $xlsx
try {
$sheet = $pkg.Workbook.Worksheets['Sheet1']
$sheet.Cells['B2'].Value = 1250000
Close-ExcelPackage $pkg -Save
}
catch {
# 失敗時は保存せず閉じてハンドルを解放
Close-ExcelPackage $pkg -NoSave
throw
}
③ 元ファイルのバックアップ(書き込み系は特に)
書き込み・上書き保存は元のブックを直接変更します。テンプレや原本を壊さないために、レシピ4(a)のように Copy-Item でコピーを作ってからコピー側に書き込むのが基本です。原本に直接書く必要がある場合は、実行前にバックアップを取ります。
# 実行前バックアップ(日時付きで退避)
$bak = "$xlsx.$(Get-Date -Format 'yyyyMMdd_HHmmss').bak.xlsx"
Copy-Item -LiteralPath $xlsx -Destination $bak
④ 日付・数値の型に注意
.Value に数値を書くときは、文字列ではなく数値そのものを代入します。$sheet.Cells['B2'].Value = "1250000"(文字列)にすると、Excel側で「文字列として保存された数値」になり、合計やソートで困ることがあります。数値は 1250000 または [int] / [double] でキャストした値を入れます。日付は [datetime] を入れると日付セルとして扱われますが、表示書式を整えたいなら .Style.Numberformat.Format = 'yyyy/mm/dd' を併用します。
# 日付を「日付値」として入れて、表示書式も指定する例
$cell = $sheet.Cells['B4']
$cell.Value = [datetime]::Today
$cell.Style.Numberformat.Format = 'yyyy/mm/dd'
⑤ 数式セルの値が空に見えるとき(.Value vs .Formula)
他システムやマクロが生成したブックでは、数式の結果(キャッシュ値)が保存されておらず、.Value が $null に見えることがあります。その場合は .Formula に数式は入っているので、スクリプト側で .Formula を確認し、必要なら Close-ExcelPackage -Calculate で再計算させます。
$cell = $sheet.Cells['B5']
Write-Host "値: $($cell.Value)" # キャッシュされた計算結果(無いと空)
Write-Host "数式: $($cell.Formula)" # 例: SUM(B2:B4)
⑥ 大きいブックの性能
ImportExcel(内部で EPPlus を使用)はブック全体をメモリに展開します。数万行×多数列の巨大ブックや、何百ファイルも一気に開く処理は、メモリと時間を食います。
- セル単位で少数のセルだけ読むなら軽い。全行集計が目的なら
Import-Excelの方が速いことが多い - 大量ファイルを順に処理するときは、1ファイルごとに必ず
Close-ExcelPackageで閉じてハンドル・メモリを解放する(開きっぱなしにしない) - それでも重いなら、Excel側で一度CSVに落として第4回の
Import-Csvルートに乗せる方が速いこともあります
⑦ Windows PowerShell 5.1 との差異
これまでの回(第3・4回)では「5.1は Export-Csv の既定エンコードが違う/UTF8BOM が使えない」が差異の中心でした。今回は事情が違います。ImportExcel のコマンド(Import-Excel / Export-Excel / Open-ExcelPackage / Close-ExcelPackage)は 5.1 と 7 で共通で、書き方を変える必要はありません。
| 項目 | PowerShell 7(pwsh) | Windows PowerShell 5.1 |
|---|---|---|
| ImportExcel の動作 | 動作する | 動作する(5.1以降に対応) |
| 主要コマンド・セルAPI |
Import-Excel / Export-Excel / Open-ExcelPackage / Cells["B2"].Value
|
同一(書き方は変わらない) |
| インストール | Install-Module ImportExcel -Scope CurrentUser |
同じ |
| 補足 |
.xlsx はバイナリのため、CSVのようなエンコード指定問題は無い |
同左 |
.xlsxはZIP圧縮されたXMLのバイナリ形式なので、CSVでつきまとった「Shift_JIS / UTF-8 / BOM」の文字コード問題がそもそも発生しません。文字コードに悩まなくて済むのは、CSVではなく.xlsxを直接扱うことの地味なメリットです。
まとめ
- ImportExcel は
Install-Module ImportExcel -Scope CurrentUserで導入(サードパーティ製・配布先に無い前提で手順を残す) -
表として読む →
Import-Excel、セル単位で読む/書く →Open-ExcelPackage+$sheet.Cells["B2"].Value - 書き込んだら
Close-ExcelPackage -Save(付け忘れると変更が消える)、読むだけなら-NoSave - セルは A1記法
Cells["B2"]でも 行列番号Cells[2,2]でも指せる(行列とも1始まり) -
.Value(生値)/.Text(表示文字列)/.Formula(数式)は別物。数式の再計算はClose-ExcelPackage -Calculate - 整形出力は
Export-Excel -AutoSize -FreezeTopRow -TableName ...(第3回と共通) - 注意点は 保存し忘れ・ファイルロック・元ファイルのバックアップ・数値/日付の型。ImportExcel は 5.1でも7でもコマンドは共通で、
.xlsxなので文字コード問題が無い
これで、毎月15〜20分かけていた「30店舗のExcelを開いてB2を転記」「テンプレに手入力」が、Excelを一度も起動せずにコマンド一本で終わります。
次回 第6回は「2つのリストを突合して差分を出す」。Compare-Object を使い、「先月と今月の名簿の差分」「マスタと実データの不一致」など、2つの一覧を突き合わせて「増えた・減った・変わった」を自動抽出するレシピを扱います。お楽しみに。
参考
- ImportExcel (GitHub: dfinke/ImportExcel)
- ImportExcel (PowerShell Gallery)
- Install-Module (PowerShellGet) - Microsoft Learn
- about_Execution_Policies (PowerShell) - Microsoft Learn
- Set-ExecutionPolicy (Microsoft.PowerShell.Security) - Microsoft Learn
- Copy-Item (Microsoft.PowerShell.Management) - Microsoft Learn
@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!