ここ数回は、PowerShellスクリプトからMicrosoft Edgeを操作し、Google Chartsを利用したグラフ画像を生成して、ファイルに保存する処理を作ってきた。プログラムとしてはそれほど長いコードではないが、PowerShellを使うだけで業務にも転用できる実用的なスクリプトが作れることがお分かりいただけたと思う。

そして足掛け5年、計257回に及んだ本連載は、今回で最後だ。グラフ生成スクリプトの最終成果物を示すと共に、PowerShellの活用について振り返り、総括とする。

→連載「PowerShell Core入門 - 基本コマンドの使い方」の過去回はこちらを参照。

グラフを生成するPowerShellスクリプト「data2chart.ps1」

ここ数回の取り組みでCSVやTSVといったデータからグラフ画像を生成するPowerShellスクリプト「data2chart.ps1」を作成した。さらに、data2chart.ps1を利用したラッパーコマンドとして「csv2barchart」「csv2piechart」「tsv2barchart」「tsv2piechart」も用意した。作業途中で生まれたスクリプトの互換目的で「csv2chart」も作った。簡単にまとめると次のようになる。

  • グラフデータ:CSV、TSV
  • グラフ種類:棒グラフ、円グラフ
  • コマンド:data2chart、csv2barchart、csv2piechart、tsv2barchart、tsv2piechart、csv2chart

集大成となる「data2chart.ps1」は次の通りだ。

#!/usr/bin/env pwsh

#========================================================================
# 引数を処理
#   -File パス            グラフのデータファイルパス
#   -PNGFile パス     生成するPNG画像のファイルパス
#   -OutFile パス     中間生成されるHTMLファイルパス
#   -GraphTitle タイトル    グラフのタイトル
#   -Width 幅              生成するPNG画像の幅
#   -Height 高さ          生成するPNG画像の高さ
#   -GraphType グラフ種類  生成するグラフの種類
#========================================================================
Param(
    [Parameter(Mandatory=$true)][String]$DataFile,
    [String]$PNGFile = (Get-Location).ToString() + '\out.png',
    [String]$OutFile = (Get-Location).ToString() + '\out.html',
    [String]$GraphTitle = 'グラフ',
    [Int]$Width = 2000,
    [Int]$Height = 1200,
    [ValidateSet('Bar','Pie')]$GraphType = 'Bar'
)

#========================================================================
# データを種類に応じて読み込み
#========================================================================
$DataFileExtension = [IO.Path]::GetExtension($DataFile)
switch ($DataFileExtension) {
    '.csv' {
        $CsvData = Import-Csv $DataFile -Header 1,2
    }
    '.tsv' {
        $CsvData = Import-Csv $DataFile -Header 1,2 -Delimiter `t
    }
    default {
        $CsvData = Import-Csv $DataFile -Header 1,2
    }
}

#========================================================================
# 気温データを取得
#========================================================================
# ヘッダを変数に格納
foreach ($Row in $CsvData) {
    $CsvHeader1 = $Row.1
    $CsvHeader2 = $Row.2
    break
}

#========================================================================
# Google Charts用HTMLの用意
#========================================================================
$GoogleChartsHTML1 = @"
<html>
  <head>
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    <script type="text/javascript">
      // Google Visualization APIおよびコアチャートパッケージを読み込み
      google.charts.load('current', {'packages':['corechart']});

      // Google Visualization API読み込み完了後に実行
      google.charts.setOnLoadCallback(drawChart);
      function drawChart() {
        // データテーブルを作成
        var data = new google.visualization.DataTable();
        data.addColumn('string', '');
        data.addColumn('number', '$CsvHeader2');
        data.addRows([
"@

$GoogleChartsHTML2 = @"
        ]);

        // チャートオプションを設定
        var options = {'title':'$GraphTitle',
                       'width':'$Width',
                       'height':'$Height'};

        // 初期化およびチャートの生成
        var chart = new google.visualization.${GraphType}Chart(document.getElementById('chart_div'));

        // チャートイメージをbase64エンコードされたPNG画像データとして出力
        google.visualization.events.addListener(chart, 'ready', function () {
          document.getElementById('chart_base64').innerHTML = chart.getImageURI();
        });

        // チャートを描画
        chart.draw(data, options);
      }
    </script>
  </head>
  <body>
    <div id="chart_div"></div>
    <div id="chart_base64"></div>
  </body>
</html>
"@

#========================================================================
# CSVデータをGoogle Chartsで使える形式へ加工
#========================================================================
$GraphData = ""
$FirstLineIs = $true
foreach ($Row in $CsvData) {
    if ($FirstLineIs) {
        $FirstLineIs = $false
        continue
    }

    $GraphData = $GraphData + "['" + $Row.1 + "'," + $Row.2 + "],"
}
$GraphData = $GraphData.trim(",")

#========================================================================
# Google Charts用のHTMLを出力
#========================================================================
$GoogleChartsHTML1                  > $OutFile
$GraphData                      >>$OutFile
$GoogleChartsHTML2                  >>$OutFile

#========================================================================
# WebDriver起動
#========================================================================
webdriver_edge_start.ps1

#========================================================================
# Microsoft EdgeでGoogle Charts用のHTMLをオープン
#========================================================================
$FileURL = "file:///" + $OutFile.Replace('\','/')

'Microsoft Edgeでグラフを描画します。'
Set-SeUrl -Url $FileURL

#========================================================================
# 描画されたグラフをBase64エンコードされたPNGデータとして取得
#========================================================================
'描画したグラフをBase64エンコードされたPNGデータとして取得します。'
$Element = Get-SeElement -By XPath -Value '//*[@id="chart_base64"]'
$Base64 = $Element.Text -replace 'data:image/png;base64,',''

#========================================================================
# Base64エンコードデータをデコードしてファイルへ保存
#========================================================================
'Base64エンコードされたPNGデータをデコードして保存します。'
$Bytes = [Convert]::FromBase64String($Base64)
[IO.File]::WriteAllBytes($PNGFile, $Bytes)

#========================================================================
# WebDriverを終了
#========================================================================
webdriver_edge_stop.ps1

#========================================================================
# 作業用の一時ファイルを削除
#========================================================================
Remove-Item $OutFile

data2chart.ps1は、途中でPowerShell Seleniumを経由してMicrosoft Edgeを操作している。この操作を行うに際し、Windows側で行っておくべき下準備や後処理などは「webdriver_edge_start.ps1」および「webdriver_edge_stop.ps1」で実行している。この2つのPowerShellスクリプトに関しては、付録として本稿の末尾に掲載したので、参考にしていただきたい。

以降にそれぞれのラッパースクリプトとその実行サンプルを掲載する。

ラッパースクリプト:csv2barchart.ps1、csv2piechart.ps1、tsv2barchart.ps1、tsv2piechart.ps1

◆csv2barchart.ps1

#!/usr/bin/env pwsh

#========================================================================
# 引数を処理
#   -CSVFile パス     グラフのCSVデータファイルパス
#   -PNGFile パス     生成するPNG画像のファイルパス
#   -OutFile パス     中間生成されるHTMLファイルパス
#   -GraphTitle タイトル    グラフのタイトル
#   -Width 幅              生成するPNG画像の幅
#   -Height 高さ          生成するPNG画像の高さ
#========================================================================
Param(
    [Parameter(Mandatory=$true)][String]$CSVFile,
    [String]$PNGFile = (Get-Location).ToString() + '\out.png',
    [String]$OutFile = (Get-Location).ToString() + '\out.html',
    [String]$GraphTitle = 'グラフ',
    [Int]$Width = 2000,
    [Int]$Height = 1200
)

#========================================================================
# data2chart.ps1を使ってグラフを生成
#========================================================================
data2chart.ps1                              `
    -DataFile   $CSVFile                    `
    -PNGFile    $PNGFile                    `
    -GraphTitle $GraphTitle                 `
    -Width      $Width                      `
    -Height     $Height                     `
    -GraphType  'Bar'

◆csv2piechart.ps1

#!/usr/bin/env pwsh

#========================================================================
# 引数を処理
#   -CSVFile パス     グラフのCSVデータファイルパス
#   -PNGFile パス     生成するPNG画像のファイルパス
#   -OutFile パス     中間生成されるHTMLファイルパス
#   -GraphTitle タイトル    グラフのタイトル
#   -Width 幅              生成するPNG画像の幅
#   -Height 高さ          生成するPNG画像の高さ
#========================================================================
Param(
    [Parameter(Mandatory=$true)][String]$CSVFile,
    [String]$PNGFile = (Get-Location).ToString() + '\out.png',
    [String]$OutFile = (Get-Location).ToString() + '\out.html',
    [String]$GraphTitle = 'グラフ',
    [Int]$Width = 2000,
    [Int]$Height = 1200
)

#========================================================================
# data2chart.ps1を使ってグラフを生成
#========================================================================
data2chart.ps1                              `
    -DataFile   $CSVFile                    `
    -PNGFile    $PNGFile                    `
    -GraphTitle $GraphTitle                 `
    -Width      $Width                      `
    -Height     $Height                     `
    -GraphType  'Pie'

◆tsv2barchart.ps1

#!/usr/bin/env pwsh

#========================================================================
# 引数を処理
#   -TSVFile パス     グラフのTSVデータファイルパス
#   -PNGFile パス     生成するPNG画像のファイルパス
#   -OutFile パス     中間生成されるHTMLファイルパス
#   -GraphTitle タイトル    グラフのタイトル
#   -Width 幅              生成するPNG画像の幅
#   -Height 高さ          生成するPNG画像の高さ
#========================================================================
Param(
    [Parameter(Mandatory=$true)][String]$TSVFile,
    [String]$PNGFile = (Get-Location).ToString() + '\out.png',
    [String]$OutFile = (Get-Location).ToString() + '\out.html',
    [String]$GraphTitle = 'グラフ',
    [Int]$Width = 2000,
    [Int]$Height = 1200
)

#========================================================================
# data2chart.ps1を使ってグラフを生成
#========================================================================
data2chart.ps1                              `
    -DataFile   $TSVFile                    `
    -PNGFile    $PNGFile                    `
    -GraphTitle $GraphTitle                 `
    -Width      $Width                      `
    -Height     $Height                     `
    -GraphType  'Bar'

◆tsv2piechart.ps1

#!/usr/bin/env pwsh

#========================================================================
# 引数を処理
#   -TSVFile パス     グラフのTSVデータファイルパス
#   -PNGFile パス     生成するPNG画像のファイルパス
#   -OutFile パス     中間生成されるHTMLファイルパス
#   -GraphTitle タイトル    グラフのタイトル
#   -Width 幅              生成するPNG画像の幅
#   -Height 高さ          生成するPNG画像の高さ
#========================================================================
Param(
    [Parameter(Mandatory=$true)][String]$TSVFile,
    [String]$PNGFile = (Get-Location).ToString() + '\out.png',
    [String]$OutFile = (Get-Location).ToString() + '\out.html',
    [String]$GraphTitle = 'グラフ',
    [Int]$Width = 2000,
    [Int]$Height = 1200
)

#========================================================================
# data2chart.ps1を使ってグラフを生成
#========================================================================
data2chart.ps1                              `
    -DataFile   $TSVFile                    `
    -PNGFile    $PNGFile                    `
    -GraphTitle $GraphTitle                 `
    -Width      $Width                      `
    -Height     $Height                     `
    -GraphType  'Pie'

互換スクリプト:csv2chart.ps1

◆csv2chart.ps1

#!/usr/bin/env pwsh

#========================================================================
# 引数を処理
#   -CSVFile パス     グラフのCSVデータファイルパス
#   -PNGFile パス     生成するPNG画像のファイルパス
#   -OutFile パス     中間生成されるHTMLファイルパス
#   -GraphTitle タイトル    グラフのタイトル
#   -Width 幅              生成するPNG画像の幅
#   -Height 高さ          生成するPNG画像の高さ
#   -GraphType グラフ種類  生成するグラフの種類
#========================================================================
Param(
    [Parameter(Mandatory=$true)][String]$CSVFile,
    [String]$PNGFile = (Get-Location).ToString() + '\out.png',
    [String]$OutFile = (Get-Location).ToString() + '\out.html',
    [String]$GraphTitle = 'グラフ',
    [Int]$Width = 2000,
    [Int]$Height = 1200,
    [ValidateSet('Bar','Pie')]$GraphType = 'Bar'
)

#========================================================================
# data2chart.ps1を使ってグラフを生成
#========================================================================
.\data2chart.ps1                            `
    -DataFile   $CSVFile                    `
    -PNGFile    $PNGFile                    `
    -GraphTitle $GraphTitle                 `
    -Width      $Width                      `
    -Height     $Height                     `
    -GraphType  $GraphType

実行サンプル

csv2barchart実行サンプル

  • csv2barchart実行サンプル

    csv2barchart実行サンプル

  • 生成されたグラフデータ

    生成されたグラフデータ

csv2piechart実行サンプル

  • csv2piechart実行サンプル

    csv2piechart実行サンプル

  • 生成されたグラフデータ

    生成されたグラフデータ

tsv2barchart実行サンプル

  • tsv2barchart実行サンプル

    tsv2barchart実行サンプル

  • 生成されたグラフデータ

    生成されたグラフデータ

tsv2piechart実行サンプル

  • tsv2piechart実行サンプル

    tsv2piechart実行サンプル

  • 生成されたグラフデータ

    生成されたグラフデータ

連載を振り返って

PowerShellはWindowsで中核を成すスクリプト言語であり、インタラクティブシェルだ。しかし、その実態は実際にはC#に近い。機能としては.NETをベースとしており、プログラミング言語的にはC#と高い親和性があり、そのシンタックスはUNIXのシェルに似ている。表現としては、「UNIXのシェル風に見せたC#」というのが実態に近いのではないかと思う。

PowerShellを使うことができれば、Windowsの提供するパワーをかなり自由に扱うことができる。“面倒な作業を自動化するちょっとしたスクリプト”として使えるだけでなく、かなり複雑なプログラミングも可能となっており、広く利用できるのがPowerShellだ。

約5年前に始まった本連載は当初、PowerShellの言語的な特性を取り上げながら、基本的なコマンドレットを紹介していた。ある程度基本的なコマンドレットやPowerShellについて解説した後は、実際にPowerShellスクリプトを組みながら実用的な使い方を示してきた。

PowerShellはいろいろな用途に使うことができるので、その全てを紹介することは難しい。連載で取り上げたのは、PowerShellの活用事例のほんの一端だが、いずれもアレンジ次第でさまざまな作業に生かせるものを選んだつもりだ。2022年11月以降は、ChatGPTを利用することでPowerShellスクリプトの自動生成も可能になった。開発の簡単さはローコードに近づきつつあるようにも思える。

本連載は今回で最終回となるが、PowerShellを使ったWindowsの効率化は、一人一人のユーザーがこれからも取り組んでいけるものである。本連載で取り上げてきた内容を参考にしつつ、業務の自動化・効率化に取り組んでもらえれば幸いだ。より多くの方に、この便利な機能を活用していただければと思う。

付録

webdriver_edge_start.ps1

#!/usr/bin/env pwsh

#========================================================================
# Microsoft Edge WebDriverを起動する
#========================================================================

#========================================================================
# 動作しているMicrosoft Edge WebDriverをすべて終了
#========================================================================
webdriver_edge_stop.ps1

#========================================================================
# Seleniumモジュールがない場合にはインストール
#========================================================================
if (-Not (Get-InstalledModule -Name Selenium 2> $Null)) {
    'Seleniumモジュールをインストールします。'
    Install-Module -Name Selenium -AllowPrerelease -Force
    Get-InstalledModule -Name Selenium
}

#========================================================================
# Microsoft Edge WebDriverを起動
#========================================================================
'Microsoft Edge WebDriverを起動します。'
$Size = '1200,800'
if  (-Not (Start-SeDriver -Browser Edge -Size $Size 2> $Null 3> $Null))
{
    #================================================================
    # Microsoft EdgeとMicrosoft Edge WebDriverのバージョンが一致して
    # いないためにドライバが動作しなかった可能性がある。
    #================================================================

    #================================================================
    # 不要なドライバプロセスを終了
    #================================================================
    webdriver_edge_stop.ps1

    #================================================================
    # Microsoft Edgeのバージョン番号
    #================================================================
    $EdgeDir='C:\Program Files (x86)\Microsoft\Edge\Application\'
    $EdgeVersion=(  Get-ChildItem -Name $EdgeDir            | 
            Where-Object { $_ -NotMatch "[a-zA-Z]+" }   |
            Select-Object -First 1              )
            # ↑ 【Select-Object -First 1の理由】
            # 更新前のバージョンと更新後のバージョンが同時に
            # 存在するタイミングがあるので、更新後のバージョン
            # のみを取得するためにSelect-Objectを実行している。

    #================================================================
    # Microsoft Edge WebDriverダウンロードURLとデプロイ先パス
    #================================================================
    $DriverURL="https://msedgedriver.azureedge.net/$EdgeVersion/edgedriver_win64.zip"

    $SeModVer=(Get-InstalledModule -Name Selenium).Version -replace "-.+$",""
    $DriverDir="$env:HOME\Documents\powershell\Modules\Selenium\$SeModVer\assemblies"
    $DriverDownloadDir="$DriverDir\_download"

    #================================================================
    # WebDriverダウンロード用の一時ディレクトリを作成
    #================================================================
    New-Item    $DriverDownloadDir -ItemType Directory -Force

    #================================================================
    # Microsoft Edgeと同じバージョンのMicrosoft Edge WebDriverを
    # ダウンロード
    #================================================================
    "Microsoft Edge WebDriver version $EdgeVersion をダウンロードします。"
    curl        -get                        `
            -o  $DriverDownloadDir\edgedriver_win64.zip `
            $DriverURL

    #================================================================
    # Microsoft Edge WebDriverをデプロイ
    #================================================================
    "Microsoft Edge WebDriver version $EdgeVersion をインストールします。"
    Expand-Archive  -Path $DriverDownloadDir\edgedriver_win64.zip   `
            -Destination $DriverDownloadDir         `
            -Force

    Copy-Item   -Path $DriverDownloadDir\msedgedriver.exe   `
            -Destination $DriverDir\msedgedriver.exe    `
            -Force

    #================================================================
    # WebDriverダウンロード用の一時ディレクトリを削除
    #================================================================
    Remove-Item $DriverDownloadDir -Recurse -Force

    #================================================================
    # Microsoft Edge WebDriverを起動する
    #================================================================
    if  (-Not (Start-SeDriver -Browser Edge -Size $Size 2> $Null 3> $Null)) 
    {
        #========================================================
        # 原因不明の起動不能
        #========================================================

        #========================================================
        # 不要なドライバプロセスを終了
        #========================================================
        webdriver_edge_stop.ps1

        Exit
    }
}
'Microsoft Edge WebDriverの起動処理完了。'

webdriver_edge_stop.ps1

#!/usr/bin/env pwsh

#========================================================================
# Microsoft Edge WebDriverを終了する
#========================================================================

#========================================================================
# WebDriverプロセスを終了
#========================================================================
if  (Get-Process -Name msedgedriver 2> $Null) 
{
    '動作しているMicrosoft Edge WebDriverを終了します。'
    Get-Process -Name msedgedriver 2> $Null

    # Microsoft Edge WebDriverを終了
    Stop-SeDriver 2> $Null

    # まだ動作しているほかのMicrosoft Edge WebDriverを終了
    if  (Get-Process -Name msedgedriver 2> $Null) 
    {
        Get-Process -Name msedgedriver 2> $Null | Stop-Process
    }

    '動作しているMicrosoft Edge WebDriverの終了処理完了。'
}