PowerShell入門⑤|CSV出力とログで作る実用スクリプト

PowerShell入門⑤|CSV出力とログで作る実用スクリプト PowerShell

スクリプトを一度書いたら、同じ仕事を、何度でも任せられます。「先月の PC の台帳、どこにあったっけ? あの Excel、誰が最後に更新したの?」

情シスあるあるです。調べた結果が、手作業のコピペで Excel に入っていると、次に誰かが調べるとき、また最初からになります。

シリーズ「情シスのための PowerShell 入門」も、いよいよ最終回です。前回(第4回:スクリプト入門と実行ポリシー)で、スクリプトを作って動かせるようになりました。今回は、調査結果を CSV に出力し、ログを残し、エラーにも備える、小さな実用スクリプトを完成させます。

結論:「記録」と「備え」があれば、スクリプトは仕事道具になる

画面に出して終わりのスクリプトは、一回きりの使い捨てです。次の3つを足すと、道具になります。

足すもの 使うコマンド 効果
結果の保存 Export-Csv 調査結果を、あとから開ける・共有できる
実行の記録 Add-Content と Get-Date いつ・何をしたかが残る
エラーへの備え try / catch と -ErrorAction Stop 失敗しても、原因が記録される

エラーを捕まえる:try / catch の落とし穴

PowerShell のエラーには、止まるエラーと、止まらないエラーがあります。try / catch で捕まえられるのは、止まるエラーだけです。

try { Get-Content .\nothing.txt } catch { 'caught' }
try { Get-Content .\nothing.txt -ErrorAction Stop } catch { '捕まえた: ' + $_.Exception.Message }
スクリプトの確認画面。存在しないファイルを Get-Content で読むと、1つ目は catch に入らず赤いエラーが出て、-ErrorAction Stop を付けた2つ目は catch で捕まる画面

1つ目は、catch に入りませんでした。赤いエラーは出るのに、'caught' は表示されません。 2つ目は、-ErrorAction Stop を付けたので、catch に入り、エラーの内容を取り出せています。

-ErrorAction Stop を付けると、止まらないエラーが、止まるエラー(try / catch で捕まえられるもの)に格上げされます。try / catch を書くなら、中のコマンドに -ErrorAction Stop を付ける、これをセットで覚えておきます。スクリプトの先頭に $ErrorActionPreference = 'Stop' と書くと、全部のコマンドに、まとめて効かせられます。

$_.Exception.Message は、catch の中で、エラーの説明文を取り出す書き方です。

日時とログ

Get-Date -Format 'yyyyMMdd'                 # 20261010 のような日付(ファイル名に使う)
Get-Date -Format 'yyyy-MM-dd HH:mm:ss'      # ログ用の時刻
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') 開始" | Add-Content -Path .\run.log -Encoding UTF8

Add-Content は、ファイルの末尾に追記します。ログの基本形は、「時刻+メッセージ」を1行ずつ足すだけです。ファイル名に日付を入れておくと、日ごとに分かれて、あとから探しやすくなります。

CSV 出力のコツ(5.1 と 7 の違い)

$info | Export-Csv -Path .\pc-info.csv -NoTypeInformation -Encoding UTF8

僕の環境で確かめた、Windows PowerShell 5.1 の注意点です。

  • -NoTypeInformation を付けないと、1行目に #TYPE ... という余計な行が入ります(5.1 の場合。PowerShell 7 では、付けなくても入りません)。
  • -Encoding UTF8 を付けると、BOM 付きの UTF-8 で保存されます(5.1 の場合)。日本語の列名が入っていても、Excel で開くときに、文字化けしにくくなります。PowerShell 7 の UTF8 は BOM なしなので、環境によっては、Excel で化けることがあります。

「どっちのバージョンで動かすか」で、出力が変わるので、CSV を共有するスクリプトは、動かす環境を決めておくのが安全です。

完成:PC の基本情報とサービスを CSV に出すスクリプト

# PC の基本情報と、動いているサービスを CSV に出力する
$ErrorActionPreference = 'Stop'
$today = Get-Date -Format 'yyyyMMdd'
$log = "C:\Temp\lab\pc-report_$today.log"

function Write-Log([string]$Message) {
    "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') $Message" | Add-Content -Path $log -Encoding UTF8
}

try {
    Write-Log '開始'
    $os = Get-CimInstance -ClassName Win32_OperatingSystem
    $info = [pscustomobject]@{
        取得日時   = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
        OS         = $os.Caption
        バージョン = $os.Version
        最終起動   = $os.LastBootUpTime.ToString('yyyy-MM-dd HH:mm')
    }
    $info | Export-Csv -Path "C:\Temp\lab\pc-info_$today.csv" -NoTypeInformation -Encoding UTF8

    $svc = Get-Service | Where-Object Status -eq 'Running' | Select-Object Name, DisplayName, Status
    $svc | Export-Csv -Path "C:\Temp\lab\services_$today.csv" -NoTypeInformation -Encoding UTF8
    Write-Log "サービス $($svc.Count) 件を出力しました"
} catch {
    Write-Log "エラー: $($_.Exception.Message)"
    throw
}

読み方は、こうです。

  1. 先頭で $ErrorActionPreference = 'Stop'(どのエラーも、止まるエラーにする)
  2. Write-Log という小さな関数で、ログに時刻つきで追記する
  3. try の中で、OS の情報(Get-CimInstance)と、動いているサービスを集めて、CSV に出す
  4. catch で、エラーの内容をログに残し、throw で、もう一度エラーとして表に出す(握りつぶさない)

Get-CimInstance は、PowerShell 7 でも使えます。古い Get-WmiObject は 7 にないので、こちらを使うのが、あとあと楽です。

フォルダーのパス(C:\Temp\lab)は、例です。実際は、スクリプトの先頭の変数に出して、1か所で変えられるようにしておくと、使い回しが楽になります。

実行すると、こうなります。

.\pc-report.ps1
pc-report.ps1 を実行し、できた CSV とログのファイル名を表示した画面。ログには開始とサービスの件数が時刻つきで残り、CSV から OS 名とバージョンを取り出せている

CSV が2つ(PC の情報とサービス一覧)、ログが1つできました。ログには、開始と、サービスを出力した件数が、時刻つきで残っています。Import-Csv で読み直すと、OS の名前(Windows 11 Pro)とバージョンが、取り出せています。第4回で触れた日本語の列名(バージョン)も、BOM 付きで保存したので、文字化けしていません。

本番で消す・止めるときは、-WhatIf を忘れない

このスクリプトは「見るだけ」ですが、運用では、ファイルを消したり、サービスを止めたりする処理も出てきます。そのときは、-WhatIf を付けて、予行演習を。付けられるコマンドでは、実行せずに、何が起きるかだけが表示されます(第2回のとおりです)。-Confirm を付けると、1件ずつ、確認を求められます。

僕のやらかし:変数が空だった日

古いログを消すスクリプトを書いたときのことです。こんな感じでした。

Remove-Item "$logDir\*" -Include *.log

$logDir に、フォルダーのパスを入れているつもりが、入力ミスで、変数が空でした。すると、"$logDir\*" は、\*、つまり、ドライブの直下を指してしまいます。

念のため、-WhatIf を付けて実行したら、画面に、ドライブ直下のファイル名が、ずらり。冷や汗が、ドッと出ました。-WhatIf を付けていなかったら、と思うと、今でも、おなかが痛くなります。

それ以来、消すコマンドの前には、いつも -WhatIf、そして、対象のパスを、画面に出して確認するクセがつきました。食べ放題で、お皿を下げる前に、「これ、まだ食べてる?」と聞くのと同じです。

全5回のまとめ:スクリプトで何ができるようになったか

回 学んだこと
① cmd との違い、動詞-名詞の命名規則、対比表
② 基本コマンド10選、-WhatIf
③ パイプラインで、絞る・並べる・数える
④ スクリプト、実行ポリシー、文字コード
⑤ 実践:CSV 出力、ログ、try / catch

次のステップ

PowerShell の基本が身についたら、次は、CSV を見る・加工する、そして、Active Directory、Microsoft 365、Entra ID、Intune の自動化へと進めます。このブログでも、続きを書いていきます。まずは、CSV を画面で並べ替え・絞り込みできる Out-GridView の記事を予定しています。

今回覚えたコマンド

コマンド 意味
try { } catch { } エラーを捕まえる
-ErrorAction Stop 止まらないエラーを、止まるエラーにする
Get-Date -Format 日時を、決まった形で出す
Add-Content ファイルの末尾に追記する
Export-Csv / Import-Csv CSV に書き出す / 読み込む
Get-CimInstance OS やハードの情報を調べる
-WhatIf / -Confirm 予行演習 / 1件ずつ確認

参考

ITもごはんも、腹八分目が長続きのコツ!

コメント