PowerShellで“Markdownテーブル”と“Excelファイル”を相互変換する方法
概要
「Excelファイル → Markdown形式のテーブル の変換」はExcel のアドオンなど仕事効率化ツールを導入することで実現できましたが、
「Markdown形式のテーブル → Excelファイル の変換」は調べたかぎり対応可能な手段が少なく感じました。
(おそらく、Markdownテーブルをプレビューして対象の表をコピーしExcelに貼り付けることで対応可能なためだと思われます。)
2024年6月現在、MarkdownテーブルからExcelファイルをツールで実現したい場合は、オンラインWebツールやPythonスクリプトが必要そうでした。
オンラインWebツールを使用すると、機密情報を含むデータが取り扱えません。
また、Pythonスクリプトを実行する場合は、難しくはありませんが実行環境を構築する必要があります。
そこで今回、Windows環境において標準搭載されているPowerShellで相互の変換を実現する方法を検証してみました。
この記事のターゲット
- Windows OS ユーザーの方
- Markdown形式のテーブル から Excelファイル に変換したい方
- Excelファイル から Markdown形式のテーブル に変換したい方
対応方法
Windows OSでは、すぐにPowerShellの使用が可能です。
今回はそれぞれ変換するPowerShellのコードでExcelファイルを扱うため、事前にExcelが制御可能となるモジュール「ImportExcel」の導入が必要。
また、そのモジュールは、管理者権限でインストールする必要があります。
事前準備
事前にモジュール「ImportExcel
」をインストールします。
モジュール「ImportExcel」のインストール
下記のコードを実行することでモジュール「ImportExcel
」を導入できます。
繰り返しにはなりますが、このコードを実行するPowerShellウィンドウは“管理者として実行”で起動(管理者権限)して実行が必要です。
#################################################################################
# 処理名 | Test-ModuleInstalled
# 機能 | モジュールの導入有無を確認
# | 参考情報:https://zenn.dev/haretokidoki/articles/0a94c0f83bd428
#--------------------------------------------------------------------------------
# 戻り値 | Boolean(True: モジュール導入済み, False: モジュール未導入)
# 引数 | MoudleName: チェックするモジュール名
#################################################################################
function Test-ModuleInstalled {
param (
[System.String]$ModuleName
)
$moduleInstalled = $false
# モジュール情報を取得
$module = (Get-Module -ListAvailable -Name $ModuleName)
if ($null -ne $module) {
$moduleInstalled = $true
}
return $moduleInstalled
}
#################################################################################
# 処理名 | Test-IsAdmin
# 機能 | PowerShellが管理者として実行しているか確認
# | 参考情報:https://zenn.dev/haretokidoki/articles/67788ca9b47b27
#--------------------------------------------------------------------------------
# 戻り値 | Boolean(True: 管理者権限あり, False: 管理者権限なし)
# 引数 | -
#################################################################################
function Test-IsAdmin {
$win_id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$win_principal = new-object System.Security.Principal.WindowsPrincipal($win_id)
$admin_permission = [System.Security.Principal.WindowsBuiltInRole]::Administrator
return $win_principal.IsInRole($admin_permission)
}
# ImportExcel モジュールのインストール
$moduleName = 'ImportExcel'
if (-Not(Test-ModuleInstalled $moduleName)) {
if (-Not(Test-IsAdmin)) {
Write-Warning '管理者権限がない為、モジュールのインストールができません。処理を中断します。'
return
}
# モジュールインストールのため、セキュリティプロトコルを“TLS1.2”に設定
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# モジュールのインストール
Install-Module -Name $moduleName -Scope CurrentUser -Force
Write-Host "$($moduleName) モジュールをインストールしました。"
} else {
Write-Host "$($moduleName) モジュールは既にインストールされています。"
}
(切り戻し手順)モジュール「ImportExcel」のアンインストール
モジュール「ImportExcel」が不要となった場合、下記のコマンドレットでモジュールのアンインストールが可能。
なお、インストール時と同様に 管理者権限が必要 です。
Uninstall-Module -Name ImportExcel -AllVersions
# アンインストール前:モジュールが存在すること
PS C:\WINDOWS\system32> Get-Module -ListAvailable -Name ImportExcel
ディレクトリ: D:\Documents\WindowsPowerShell\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 7.8.9 ImportExcel {Add-ConditionalFormatting, Add-ExcelChart, Add-ExcelDataV...
PS C:\WINDOWS\system32>
# アンインストール実行
PS C:\WINDOWS\system32> Uninstall-Module -Name ImportExcel -AllVersions
PS C:\WINDOWS\system32>
# アンインストール後:モジュールがアンインストールされたこと
PS C:\WINDOWS\system32> Get-Module -ListAvailable -Name ImportExcel
PS C:\WINDOWS\system32>
参考情報:モジュール「ImportExcel」のアンインストールでエラーが発生する場合
今回紹介したモジュール「ImportExcel」を使用したコードの直後にアンインストールしようとすると、エラーが発生します。
実際に発生したエラーは下記のとおりです。
PS C:\WINDOWS\system32> Uninstall-Module ImportExcel
警告: バージョン '7.8.9' のモジュール 'ImportExcel'
は現在使用中です。アプリケーションを終了した後で、操作をやり直してください。
PackageManagement\Uninstall-Package : モジュール 'ImportExcel' は現在使用中か、必要なアクセス許可がありません。
発生場所 C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:2194 文字:21
+ ... $null = PackageManagement\Uninstall-Package @PSBoundParameters
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Microsoft.Power...ninstallPackage:UninstallPackage) [Uninstall-Packag
e]、Exception
+ FullyQualifiedErrorId : ModuleIsInUse,Uninstall-Package,Microsoft.PowerShell.PackageManagement.Cmdlets.Uninstall
Package
PS C:\WINDOWS\system32>
エラーが発生するタイミングは、モジュール「ImportExcel」を使用した時のみだと複数回検証し確認済み。
おそらくモジュールの解放処理で問題があるようで、このような事象が発生していると思われる。
明示的にモジュールを開放する方法を調べましたが、見つかりませんでした。
同じ事象が発生した場合、モジュールを読み込み実行したコンソール(PowerShellウィンドウ や Windowsターミナルなど)を閉じて、
再度コンソールを立ち上げた後に Uninstall-Module
コマンドレットを実行してください。
Markdown-Table から Excelファイル に変換できるPowerShellのコード
関連するFunctionを含めたコード。メインのFunctionは「Convert-MarkdownTableToExcel
」です。
PowerShellの制約上、メインのFunctionは後方にコーディングしています。
#################################################################################
# 処理名 | Test-FileLocked
# 機能 | ファイルが開かれているか(ロック状態を)確認
# | 参考情報:https://zenn.dev/haretokidoki/articles/b4f4399570000a
#--------------------------------------------------------------------------------
# 戻り値 | Boolean
# | True: ファイルを開いている状態(ロック状態), False: 開いていない状態
# 引数 | -
#################################################################################
function Test-FileLocked {
param (
[Parameter(Mandatory=$true)][System.String]$Path
)
if (-Not(Test-Path $Path)) {
Write-Error '対象ファイルが存在しません。' -ErrorAction Stop
}
# 相対パスだとOpenメソッドが正常動作しない為、絶対パスに変換
$fullPath = (Resolve-Path -Path $Path -ErrorAction SilentlyContinue).Path
$fileLocked = $false
try {
# 読み取り専用でファイルを開く処理を実行
$fileStream = [System.IO.File]::Open($fullPath, 'Open', 'ReadWrite', 'None')
}
catch {
# ファイルが開けない場合、ロック状態と判断
$fileLocked = $true
}
finally {
if ($null -ne $fileStream) {
$fileStream.Close()
}
}
return $fileLocked
}
# コンテンツを変換する処理
function Get-ExcelTable {
param (
[Parameter(Mandatory=$true)]
[string]$FromMarkdownPath
)
# 内容を読み取る
$inputContents = (Get-Content $FromMarkdownPath -Raw)
# 表の開始と終了を検出する正規表現
$tableRegex = '(?smi)^\|.*(?<!\\)\|.*$'
$headerSplitRegex = '^\|?(.*?)(?<!\\)\|$'
$rowSplitRegex = '^\|(.+?)(?<!\\)\|$'
# 表の内容を抽出します
$excelTables = @()
[regex]::Matches($inputContents, $tableRegex) | ForEach-Object {
$tableText = $_.Value
# ヘッダーを解析します
$rowCount = 0
$rows = @()
$tableText -split "`n" | ForEach-Object {
if ($rowCount -eq 0) {
$headers = [regex]::Split($_, '(?<!\\)\|')
$headers = $headers[1..($headers.Length-2)] # 最初と最後の空の要素を削除
$rowCount += 1
}
elseif ($rowCount -eq 1) {
# ヘッダーとコンテンツの区切り行をスキップ
$rowCount += 1
}
else {
$row = [regex]::Split($_, '(?<!\\)\|')
$row = $row[1..($row.Length-2)] # 最初と最後の空の要素を削除
# ヘッダー項目数 と データ項目数を比較
if ($row.Count -eq $headers.Count) {
$rows += ,$row
}
else {
# 空行以外
if (-Not([System.String]::IsNullOrEmpty($_.Trim()))) {
Write-Host "ヘッダーの項目数と合わなかった為、下記のデータをスキップしました。[ヘッダー項目数: $($headers.Count), データ項目数: $($row.Count)]"
Write-Host '```:markdown'
Write-Host "$_"
Write-Host '```'
}
}
}
}
# 各行をオブジェクトに変換します
$rows | ForEach-Object {
$rowObject = New-Object psobject
for ($i = 0; $i -lt $headers.Count; $i++) {
$rowObject | Add-Member -MemberType NoteProperty -Name $headers[$i].Trim() -Value $_[$i].Trim()
}
$excelTables += ,$rowObject
}
}
return $excelTables
}
# Markdown形式のテーブル から Excelファイル に変換するFunciton
function Convert-MarkdownTableToExcel {
param (
[Parameter(Mandatory=$true)]
[System.String]$FromMarkdownPath,
[Parameter(Mandatory=$true)]
[System.String]$ToExcelPath
)
# Markdown形式のテーブル を Excel
$excelTables = (Get-ExcelTable -FromMarkdownPath $FromMarkdownPath)
if ($null -eq $excelTables) {
Write-Error 'ファイルを読み取りましたが、データを読み取れませんでした。'
return
}
if (Test-Path -Path $ToExcelPath) {
# ファイルがロックされているかテストします
if (Test-FileLocked -Path $ToExcelPath) {
Write-Warning 'Excelファイルが開かれています。ファイルを閉じてから再試行してください。'
return
}
# 入力メッセージ
$userInput = Read-Host 'ファイルを上書きしますか? (Yes/No)'
$yesPatterns = '[Yy][Ee][Ss]|[Yy]'
$noPatterns = '[Nn][Oo]|[Nn]'
$isYes = $false
# 試行回数: 3回
$maxCount = 2
for ($i = 0; $i -lt $maxCount; $i++) {
if ($userInput -match $yesPatterns) {
$isYes = $true
break
}
else {
Write-Host '上書き保存をキャンセルしました。'
return
}
}
if (-Not ($isYes)) {
Write-Warning '試行回数を超過しました。処理を中断します。'
return
}
# ユーザーが「はい」を選択した場合、ファイルを上書き保存します
try {
$excelTables | Export-Excel -Path $toExcelPath -Show
Write-Host '上書き保存しました。'
}
catch {
Write-Error '上書き保存でエラーが発生しました。'
}
}
else {
# 新規保存
try {
$excelTables | Export-Excel -Path $toExcelPath -Show
Write-Host "新規保存しました。"
}
catch {
Write-Error '新規保存でエラーが発生しました。'
}
}
}
下記でメインのFunction「Convert-MarkdownTableToExcel
」を実行。
# 実行
$markdownPath = 'D:\Downloads\inputMarkdownTable.md'
$excelPath = 'D:\Downloads\outputExcel.xlsx'
Convert-MarkdownTableToExcel $markdownPath $excelPath
補足情報:MarkdownテーブルからExcelファイルへの変換時の入出力データ一式
- 入力ファイル:テーブルのみ記述したMarkdown形式のファイル
- 出力ファイル:入力ファイルのMarkdownファイルを元に変換したExcelファイル
Excelファイル から Markdown-Table に変換できるPowerShellのコード
関連するFunctionを含めたコード。メインのFunctionは「Convert-ExcelToMarkdownTable
」です。
PowerShellの制約上、メインのFunctionは後方にコーディングしています。
#################################################################################
# 処理名 | Test-FileLocked
# 機能 | ファイルが開かれているか(ロック状態を)確認
# | 参考情報:https://zenn.dev/haretokidoki/articles/b4f4399570000a
#--------------------------------------------------------------------------------
# 戻り値 | Boolean
# | True: ファイルを開いている状態(ロック状態), False: 開いていない状態
# 引数 | -
#################################################################################
function Test-FileLocked {
param (
[Parameter(Mandatory=$true)][System.String]$Path
)
if (-Not(Test-Path $Path)) {
Write-Error '対象ファイルが存在しません。' -ErrorAction Stop
}
# 相対パスだとOpenメソッドが正常動作しない為、絶対パスに変換
$fullPath = (Resolve-Path -Path $Path -ErrorAction SilentlyContinue).Path
$fileLocked = $false
try {
# 読み取り専用でファイルを開く処理を実行
$fileStream = [System.IO.File]::Open($fullPath, 'Open', 'ReadWrite', 'None')
}
catch {
# ファイルが開けない場合、ロック状態と判断
$fileLocked = $true
}
finally {
if ($null -ne $fileStream) {
$fileStream.Close()
}
}
return $fileLocked
}
# コンテンツを変換する処理
function Get-MarkdownTable {
param (
[Parameter(Mandatory=$true)]
[string]$FromExcelPath
)
# 内容を読み取る
$inputContents = (Import-Excel -Path $FromExcelPath)
# Markdown形式のテーブルを構築します
$markdownTables = ''
$headers = $inputContents[0].PSObject.Properties.Name
# ヘッダー行を追加します
$markdownTables += '| ' + ($headers -join ' | ') + ' |' + "`r`n"
# 区切り行を追加します
$markdownTables += '| ' + (($headers | ForEach-Object { '---' }) -join ' | ') + ' |' + "`r`n"
# データ行を追加します
for ($i = 0; $i -lt $inputContents.Count; $i++) {
$rowData = @()
foreach ($header in $headers) {
$cellValue = $inputContents[$i]."$header"
$rowData += $cellValue
}
$markdownTables += '| ' + ($rowData -join ' | ') + ' |'
# 最後の行以外は改行を追加
if ($i -lt $inputContents.Count - 1) {
$markdownTables += "`r`n"
}
}
return $markdownTables
}
# Exelファイル から Markdown形式のテーブル に変換するFunction
function Convert-ExcelToMarkdownTable {
param (
[Parameter(Mandatory=$true)]
[System.String]$FromExcelPath,
[Parameter(Mandatory=$true)]
[System.String]$ToMarkdownPath
)
$markdownTables = (Get-MarkdownTable -FromExcelPath $FromExcelPath)
if ($null -eq $markdownTables) {
Write-Error 'ファイルを読み取りましたが、データを読み取れませんでした。'
return
}
if (Test-Path -Path $ToMarkdownPath) {
if (Test-FileLocked -Path $ToMarkdownPath) {
Write-Warning 'Markdownファイルが開かれています。ファイルを閉じてから再試行してください。'
return
}
# 入力メッセージ
$userInput = Read-Host 'ファイルを上書きしますか? (Yes/No)'
$yesPatterns = '[Yy][Ee][Ss]|[Yy]'
$noPatterns = '[Nn][Oo]|[Nn]'
$isYes = $false
# 試行回数: 3回
$maxCount = 2
for ($i = 0; $i -lt $maxCount; $i++) {
if ($userInput -match $yesPatterns) {
$isYes = $true
break
}
else {
Write-Host '上書き保存をキャンセルしました。'
return
}
}
if (-Not ($isYes)) {
Write-Warning '試行回数を超過しました。処理を中断します。'
return
}
# ユーザーが「はい」を選択した場合、ファイルを上書き保存します
try {
$markdownTables | Out-File -FilePath $ToMarkdownPath -Encoding utf8
Write-Host '上書き保存しました。'
}
catch {
Write-Error '上書き保存でエラーが発生しました。'
}
}
else {
# 新規保存
try {
$markdownTables | Out-File -FilePath $ToMarkdownPath -Encoding utf8
Write-Host "新規保存しました。"
}
catch {
Write-Error '新規保存でエラーが発生しました。'
}
}
}
下記でメインのFunctionは「Convert-ExcelToMarkdownTable
」を実行。
# 実行
$excelPath = 'D:\Downloads\inputExcel.xlsx'
$markdownPath = 'D:\Downloads\outputMarkdownTable.md'
Convert-ExcelToMarkdownTable $excelPath $markdownPath
補足情報:MarkdownテーブルからExcelファイルへの変換時の入出力データ一式
- 入力ファイル:セル位置「A1」にヘッダー、「A2」以降からコンテンツがあるExcelファイル
- 出力ファイル:入力ファイルのExcelファイルを元に変換したMarkdownファイル
まとめ
- 下記のFunctionを作成することができた
- Markdown形式のテーブル を元に Excelファイル を出力するFunction
- Excelファイル を元に Markdown形式のテーブル を出力するFunction
- 入出力の要件が細かくある場合はカスタマイズが必要
とくにExcelファイルが入力データとなる場合はヘッダーやコンテンツの書き出し位置など必要に応じてコードを修正してください。 - 今後、ここZennの記事を作成する際にこのコードを活用しようと思います。
関連記事
Discussion