Powershell: powercfg -xml の出力ファイルを解析する

powercfg.exe コマンドを使うと、ラップトップPC内蔵バッテリーの充電容量が新品の時と比べてどの程度の消耗度になっているかを知ることが出来ることがわかりました。

企業などの組織で、同一構成のPCを使っているなら、組織内ラップトップPC全体に対してリモートから powercfg.exe コマンドを発行し、バッテリーステータス情報を収集することが出来ることもわかりました。

powercfg コマンドの出力結果はデフォルト HTML 形式であるため、バッテリーステータス調査対象が数台なら、一つ一つWebブラウザで開いて容量を確認してもそれほど手間になりませんが、対象ラップトップPCが10台を越えるならファイルを開く手間や、データ写し間違いの可能性を避けるためにプログラムで処理してしまうべきでしょう。

powercfg コマンドは -xml オプションを使えばプログラム処理向きの XML 形式出力に切り替えることができるため xml 書式で出力し、Powershell で読み込んで必要なフィールドだけをカンマ区切りファイルで出力できないか試してみました。ファイル名は、仮に energy.xml としておきます。

まず、energy.xml の中身は次のようになっています。

<?xml version="1.0" encoding="utf-8"?>
<EnergyReport xmlns="http://schemas.microsoft.com/energy/2007">
  <ReportInformation>
    <ReportGuid>502c2315-fbd7-4bbf-8920-74980e7a627e</ReportGuid>
    <ReportVersion>1.0</ReportVersion>
    <ScanTime>2017-06-03T09:30:44Z</ScanTime>
    <ScanDuration>PT1S</ScanDuration>
  </ReportInformation>
  <Troubleshooter guid="c78b52e3-9cd7-4802-979c-57ca1283dafa">
    <Name>システム情報</Name>
    <AnalysisLog>
      <LogEntry guid="3d053cee-9926-4ac0-a4e1-63ab80c63af0">
        <Name>システム情報</Name>
        <Severity>Informational</Severity>
        <Description>システム情報の詳細</Description>
        <Details>
          <Detail guid="300b5d9f-434e-45b0-80d2-4bd55b168ef3">
            <Name>コンピューター名</Name>

中略

  <Troubleshooter guid="5f159d5d-4dec-4caf-81e5-645d77e05c84">
    <Name>バッテリ</Name>
    <AnalysisLog>
      <LogEntry guid="76e4b077-bb50-4000-9563-7f5aa0c9dc26">
        <Name>バッテリ情報</Name>
        <Severity>Informational</Severity>
        <Description></Description>
        <Details>
          <Detail guid="118bf18a-13d4-4226-b207-f2ae1638de8b">
            <Name>バッテリ ID</Name>
            <Value>1288PA381</Value>
          </Detail>
          <Detail guid="85b01a9b-bb18-4f71-8d12-6f7dec4b3705">
            <Name>製造元</Name>
            <Value></Value>
          </Detail>
          <Detail guid="1b9d5465-63f4-4c5d-8259-93effc455084">
            <Name>製造日</Name>
            <Value></Value>
          </Detail>
          <Detail guid="2229029f-aa9e-4591-989a-32223a114538">
            <Name>シリアル番号</Name>
            <Value>1288</Value>
          </Detail>
          <Detail guid="24e6973f-f544-4a33-876d-359ebc56336e">
            <Name>種類</Name>
            <Value>Li-i</Value>
          </Detail>
          <Detail guid="8676c4f7-8918-4007-af80-76e308ca983c">
            <Name>長期</Name>
            <Value>1</Value>
          </Detail>
          <Detail guid="8800d772-7da3-48a1-b1e4-fc86df0e49cf">
            <Name>シールド</Name>
            <Value>0</Value>
          </Detail>
          <Detail guid="17d29a01-f010-4f66-bf60-c121e35cfc2b">
            <Name>サイクル数</Name>
            <Value></Value>
          </Detail>
          <Detail guid="beb3f51a-9d89-42ad-81c4-5f9b7f682fa4">
            <Name>容量の設計</Name>
            <Value>48600</Value>
          </Detail>
          <Detail guid="b42aa79e-8ee8-44ae-8a11-5fe87cf2822b">
            <Name>前回の完全充電</Name>
            <Value>44301</Value>
          </Detail>
        </Details>
      </LogEntry>
    </AnalysisLog>
  </Troubleshooter>
以下省略

知りたい情報は、上記の赤の部分あたり。HTML書式出力ファイルもそうですが、スキャン時PCの状態によって出力行数や容量表示位置が異なるので、Unix の grep や tail などのように、先頭から何行目、最後尾から何行目 という切り出しが使えません。XML書式に対応した処理が必要です。

「powershell xml」で検索してみると沢山の情報が見つかります。

まず、(Get-Content xmlファイル名) の前に[xml] を付ければ XML ファイルをPowershell の中に取り込めるってことです。これを変数に取り込んでしまえば、あとは必要な位置の情報を取り出せるます。超簡単。(と思うようになるまで数時間かかっているんですけどね。)

$xml = [xml](Get-Content energy.xml)

超簡単と書きましたけど、まずは必要な数値がどこにあるのかを知るために、XMLエディタが必要になります。普通のエディタでも大丈夫ですけど、タグの数をいちいち数えないといけないし、数え間違いがあると思ったような出力にならないので、energy.xml くらいのサイズになると、XMLエディタは必要。

次の例は energy.xml ファイルを、Microsoft の XML NotePad 2007 で開いたところ。
上のXMLファイルに含まれるバッテリー情報の、”シリアル番号”、”容量の設計”、”前回の完全充電” がどこにあるのかが一発でわかります。トップ > 7番目のTroubleshooter > AnalysisLog > LogEntry> Details > 4番目と9番目と10番目のDetail の要素に欲しい情報があるとわかります。

energy.xml を XML Notepad 2007 で開く

この階層構造を Powershell で表現すると次のようになります。途中の[6] は Powershell の配列は 0 から始まるので、7番目のTroubleshooter は Troubleshooter[6]となります。

$xml.EnergyReport.Troubleshooter[6].AnalysisLog.LogEntry.Details.Detail

これを変数、例えば $battery に入れてしまえば、以降 $battery[n] で 10個のDetailにアクセスできるようになります。

$battery = $xml.EnergyReport.Troubleshooter[6].AnalysisLog.LogEntry.Details.Detail

4番目と9番目と10番目の情報が欲しいので、[3],[8],[9] の Value 要素を取り出せばOK。CSVとして一行に出力するには次のように書けばOK。

$battery[3].value + "," + $battery[8].value + "," + $battery[9].value

以上、一つの XML ファイルを処理しましたけど、一台のPCの情報を取り出すためにわざわざ Powershell でXMLファイルを処理するのは馬鹿馬鹿しい話です。energy.xml じゃなくて、収集したPCの出力ファイルをpcname.xml として一つのフォルダーに保存し、foreach ループを使って csvファイルを作ってしまうような処理を行うべきでしょう。

例えば、次のような感じでループ処理させることができます。(デバッグしていないので何処かに間違いがあるかも。)

$csv_file = "battery_csv.txt" #最終的にこのファイルに情報が書き込まれる
$working_dir = "c:/temp/battery/reports" #あらかじめこのフォルダーに、pcname.xml でpowercfg の出力結果を入れておく
cd $working_dir 
foreach ( $h in (ls -name *.xml)){   # $h にホスト名;PC名が入る。
            $xml = [xml](Get-Content $h)
            $battery = $xml.EnergyReport.Troubleshooter[6].AnalysisLog.LogEntry.Details.Detail
            $pcname = $h -Replace ".xml",""   # ファイル名から.xml を取り除き PC名とする
            $pcname + "," + $battery[3].value + "," + $battery[8].value + "," + $battery[9].value | Tee-Object -Append -FilePath $csv_file
}

注意事項としては、メーカーやモデルによって、XMLコンテナに入っている情報は異なりますので、組織内のラップトップPCが複数の異なるメーカーで構成されているなら、モデル毎にあらかじめサンプル出力を取ってスクリプトの結果を確認しておかないと、意味のないデータを一生懸命集めてしまうことになるかも知れませんので注意が必要です。今回のデータは、東芝 Dynabook のみ有効で、他のメーカーで試すと別の結果になる可能性があります。

まとめ

Powershell と XML の相性はとってもよろしいようです。

Powershell は、データ元がWindowsなら、定型書式の中から、特定の要素やデータを切り出すのにとっても便利だと感じます。
Powershell は、標準でOSに組み込まれているのがいいところです。

コメントを残す