【PowerShell入門】Write-Debugの使い方|スクリプトの中身を見える化するデバッグ術

Windows

PowerShellスクリプトを書いていて、こんなことで困ったことはありませんか?

  • 「今この変数にはなにが入っているの?」
  • 「この条件分岐に入っているかな?」
  • 「スクリプトがどこで止まっているの?」
  • 「なぜ期待した結果にならないの?」

そんなときに便利なのが、Write-Debugコマンドです。

Write-Debugは、スクリプトの中で任意のタイミングでデバッグ用メッセージを出力するためのコマンドです。

これをつかえば、開発中にコードの流れを把握したり、トラブルシューティングがぐっと楽になります。

この記事では、Write-Debugの基本的なつかい方から実践的な活用方法まで、初心者向けに説明します。

スポンサーリンク

Write-Debugってなに?

説明

Write-Debugは、PowerShellのスクリプトや関数の中で、デバッグメッセージを出力するためのコマンドレットです。

普通のWrite-Outputとちがって、普段は表示されません

-Debugスイッチが有効なときだけ表示される「隠れたメッセージ」のようなものです。

Write-Debugの特徴

  • 開発者専用:普段は見えない、開発時だけのメッセージ
  • 条件付き表示:必要なときだけ表示できる
  • デバッグ専用:本番運用時は影響しない
  • 詳細な状況把握:変数の値や処理の流れを確認できる

基本的な書き方

Write-Debug "ここにデバッグメッセージを書く"

ただし、これだけでは表示されません。表示するには特別な設定が必要です。

Write-Debugを表示する方法

方法1:関数で-Debugスイッチをつかう

ステップ1:関数を作成

function Test-Debug {
    [CmdletBinding()]  # この行が重要
    param()
    
    Write-Debug "デバッグメッセージです"
    Write-Output "通常の出力です"
}

ステップ2:-Debugスイッチで実行

Test-Debug -Debug

実行結果

DEBUG: デバッグメッセージです
通常の出力です

ステップ3:普通に実行した場合

Test-Debug

実行結果

通常の出力です

デバッグメッセージは表示されません。

方法2:$DebugPreferenceをつかう

# デバッグメッセージを常に表示するように設定
$DebugPreference = "Continue"

Write-Debug "これは常に表示されます"
Write-Output "通常の出力"

# 設定を元に戻す
$DebugPreference = "SilentlyContinue"

実行結果

DEBUG: これは常に表示されます
通常の出力

$DebugPreferenceの値

設定値説明
SilentlyContinue表示しない(デフォルト)
Continue表示して処理を続行
Inquire表示して続行するか確認
Stop表示してエラーで停止

実践的なWrite-Debugの活用例

例1:変数の値を確認する

説明

スクリプトの実行中に、変数にどんな値が入っているかを確認したい場合の例です。

スクリプト

function Calculate-Tax {
    [CmdletBinding()]
    param(
        [int]$Price,
        [double]$TaxRate = 0.1
    )
    
    Write-Debug "入力された価格: $Price"
    Write-Debug "税率: $TaxRate"
    
    $tax = $Price * $TaxRate
    Write-Debug "計算された税額: $tax"
    
    $total = $Price + $tax
    Write-Debug "合計金額: $total"
    
    return $total
}

# 使用例
$result = Calculate-Tax -Price 1000 -TaxRate 0.08 -Debug
Write-Output "最終結果: $result"

実行結果

DEBUG: 入力された価格: 1000
DEBUG: 税率: 0.08
DEBUG: 計算された税額: 80
DEBUG: 合計金額: 1080
最終結果: 1080

例2:条件分岐の流れを確認する

説明

if文やswitch文で、どの条件に合致したかを確認したい場合の例です。

スクリプト

function Check-Grade {
    [CmdletBinding()]
    param(
        [int]$Score
    )
    
    Write-Debug "=== 成績判定開始 ==="
    Write-Debug "入力されたスコア: $Score"
    
    if ($Score -ge 90) {
        Write-Debug "90点以上の条件に合致"
        $grade = "A"
    } elseif ($Score -ge 80) {
        Write-Debug "80-89点の条件に合致"
        $grade = "B"
    } elseif ($Score -ge 70) {
        Write-Debug "70-79点の条件に合致"
        $grade = "C"
    } elseif ($Score -ge 60) {
        Write-Debug "60-69点の条件に合致"
        $grade = "D"
    } else {
        Write-Debug "60点未満の条件に合致"
        $grade = "F"
    }
    
    Write-Debug "決定された成績: $grade"
    Write-Debug "=== 成績判定終了 ==="
    
    return $grade
}

# 使用例
$grades = @(95, 82, 75, 58)
foreach ($score in $grades) {
    $result = Check-Grade -Score $score -Debug
    Write-Output "スコア $score → 成績 $result"
    Write-Output "---"
}

例3:ループ処理の進行状況を確認する

説明

繰り返し処理で、現在何回目の処理をしているかや、各回の処理内容を確認したい場合の例です。

スクリプト

function Process-Files {
    [CmdletBinding()]
    param(
        [string[]]$FileNames
    )
    
    Write-Debug "=== ファイル処理開始 ==="
    Write-Debug "処理対象ファイル数: $($FileNames.Count)"
    
    $processed = 0
    foreach ($fileName in $FileNames) {
        $processed++
        Write-Debug "[$processed/$($FileNames.Count)] 処理中: $fileName"
        
        # ファイル存在チェック(例)
        if (Test-Path $fileName) {
            Write-Debug "  ✓ ファイルが存在します"
            # 実際の処理をここに書く
            Write-Debug "  → 処理完了"
        } else {
            Write-Debug "  ✗ ファイルが見つかりません"
        }
    }
    
    Write-Debug "=== ファイル処理終了 ==="
    Write-Output "処理完了: $processed ファイル"
}

# 使用例
$files = @("test1.txt", "test2.txt", "test3.txt")
Process-Files -FileNames $files -Debug

例4:関数の開始と終了を記録する

説明

複雑なスクリプトで、どの関数がいつ呼ばれて、いつ終了したかを追跡したい場合の例です。

スクリプト

function Get-UserInfo {
    [CmdletBinding()]
    param(
        [string]$UserName
    )
    
    Write-Debug ">>> Get-UserInfo開始 ($(Get-Date -Format 'HH:mm:ss'))"
    Write-Debug "対象ユーザー: $UserName"
    
    # 仮のユーザー情報取得処理
    Start-Sleep -Seconds 1  # 処理時間のシミュレーション
    
    $userInfo = @{
        Name = $UserName
        Department = "IT部"
        Role = "エンジニア"
    }
    
    Write-Debug "取得した情報: $($userInfo | ConvertTo-Json -Compress)"
    Write-Debug "<<< Get-UserInfo終了 ($(Get-Date -Format 'HH:mm:ss'))"
    
    return $userInfo
}

function Generate-Report {
    [CmdletBinding()]
    param(
        [string[]]$UserNames
    )
    
    Write-Debug ">>> Generate-Report開始 ($(Get-Date -Format 'HH:mm:ss'))"
    
    $report = @()
    foreach ($user in $UserNames) {
        Write-Debug "ユーザー情報取得中: $user"
        $info = Get-UserInfo -UserName $user -Debug
        $report += $info
    }
    
    Write-Debug "レポート生成完了: $($report.Count) 件"
    Write-Debug "<<< Generate-Report終了 ($(Get-Date -Format 'HH:mm:ss'))"
    
    return $report
}

# 使用例
$users = @("田中", "佐藤", "山田")
$result = Generate-Report -UserNames $users -Debug

Write-Debugと他の出力コマンドのちがい

比較表

コマンド表示される条件主な用途
Write-Outputいつも表示通常の結果出力
Write-Hostいつも表示ユーザー向けメッセージ指定可能
Write-Warningいつも表示警告メッセージ
Write-Errorいつも表示エラーメッセージ
Write-Verbose-Verbose時のみ詳細な処理情報
Write-Debug-Debug時のみデバッグ情報白(DEBUG:付き)

使い分けの例

function Demo-OutputCommands {
    [CmdletBinding()]
    param(
        [string]$UserName
    )
    
    Write-Output "これは結果です: $UserName"          # 常に表示(結果)
    Write-Host "処理を開始します..." -ForegroundColor Green    # 常に表示(進捗)
    Write-Warning "注意: テスト環境です"              # 常に表示(警告)
    Write-Verbose "詳細: ユーザー検証中..."          # -Verbose時のみ
    Write-Debug "デバッグ: 変数チェック完了"          # -Debug時のみ
    
    if (-not $UserName) {
        Write-Error "ユーザー名が指定されていません"   # エラー時のみ
        return
    }
}

# 実行例
Demo-OutputCommands -UserName "テストユーザー"                    # 基本出力のみ
Demo-OutputCommands -UserName "テストユーザー" -Verbose            # 詳細情報も表示
Demo-OutputCommands -UserName "テストユーザー" -Debug              # デバッグ情報も表示
Demo-OutputCommands -UserName "テストユーザー" -Verbose -Debug     # すべて表示

スクリプトファイルでのWrite-Debug

スクリプトファイルの例

ファイル名:debug-sample.ps1

[CmdletBinding()]
param(
    [string]$InputFile,
    [string]$OutputFile
)

Write-Debug "=== スクリプト開始 ==="
Write-Debug "入力ファイル: $InputFile"
Write-Debug "出力ファイル: $OutputFile"

# 入力ファイルの存在確認
if (-not (Test-Path $InputFile)) {
    Write-Debug "エラー: 入力ファイルが見つかりません"
    Write-Error "ファイルが見つかりません: $InputFile"
    exit 1
}

Write-Debug "入力ファイルが確認されました"

# ファイル処理(例)
$content = Get-Content $InputFile
Write-Debug "読み込み行数: $($content.Count)"

# 何らかの処理
$processedContent = $content | ForEach-Object {
    Write-Debug "処理中の行: $_"
    $_.ToUpper()  # 大文字に変換
}

# 出力
$processedContent | Out-File $OutputFile
Write-Debug "出力完了: $OutputFile"
Write-Debug "=== スクリプト終了 ==="

実行方法

# 通常実行(デバッグメッセージなし)
.\debug-sample.ps1 -InputFile "input.txt" -OutputFile "output.txt"

# デバッグモードで実行
.\debug-sample.ps1 -InputFile "input.txt" -OutputFile "output.txt" -Debug

デバッグのベストプラクティス

1. 適切な粒度でメッセージを出力

良い例

Write-Debug "=== 関数開始: Calculate-Discount ==="
Write-Debug "入力パラメータ: Price=$Price, Rate=$Rate"
Write-Debug "計算結果: Discount=$discount"
Write-Debug "=== 関数終了: Calculate-Discount ==="

悪い例(出力しすぎ)

Write-Debug "変数aに1を代入"
Write-Debug "変数bに2を代入"
Write-Debug "a + b を計算"
Write-Debug "結果は3"

2. 構造化されたメッセージ

function Format-DebugMessage {
    param(
        [string]$Level,
        [string]$Function,
        [string]$Message
    )
    
    $timestamp = Get-Date -Format "HH:mm:ss.fff"
    Write-Debug "[$timestamp] [$Level] [$Function] $Message"
}

function Sample-Function {
    [CmdletBinding()]
    param()
    
    Format-DebugMessage -Level "INFO" -Function "Sample-Function" -Message "処理開始"
    Format-DebugMessage -Level "DATA" -Function "Sample-Function" -Message "データ取得完了"
    Format-DebugMessage -Level "INFO" -Function "Sample-Function" -Message "処理終了"
}

3. 条件付きデバッグ

function Advanced-Debug {
    [CmdletBinding()]
    param(
        [switch]$DetailedDebug
    )
    
    Write-Debug "基本的なデバッグ情報"
    
    if ($DetailedDebug) {
        Write-Debug "詳細なデバッグ情報"
        Write-Debug "メモリ使用量: $((Get-Process -Id $PID).WorkingSet64 / 1MB) MB"
        Write-Debug "実行時間: $(Get-Date)"
    }
}

トラブルシューティング

よくある問題と解決方法

問題1:Write-Debugが表示されない

原因:[CmdletBinding()]が抜けている

# ❌ 悪い例
function Test {
    Write-Debug "表示されません"
}

# ✅ 良い例
function Test {
    [CmdletBinding()]
    param()
    Write-Debug "表示されます"
}

問題2:スクリプトで-Debugが効かない

原因:スクリプトの先頭に[CmdletBinding()]がない

# ❌ 悪い例
param($Name)
Write-Debug "Hello $Name"

# ✅ 良い例
[CmdletBinding()]
param($Name)
Write-Debug "Hello $Name"

問題3:デバッグメッセージが多すぎる

解決:条件分岐やレベル分けを活用

$DebugLevel = "Basic"  # "Basic", "Detailed", "Verbose"

if ($DebugLevel -eq "Detailed" -or $DebugLevel -eq "Verbose") {
    Write-Debug "詳細なデバッグ情報"
}

if ($DebugLevel -eq "Verbose") {
    Write-Debug "さらに詳しい情報"
}

まとめ

Write-Debugの重要ポイント

  1. 条件付き表示-Debugスイッチまたは$DebugPreferenceで制御
  2. 開発専用:本番運用時は表示されないので安全
  3. 詳細な追跡:変数の値、処理の流れ、タイミングを確認
  4. 適切な粒度:必要な情報だけを出力

使い分けガイド

目的使用するコマンド
最終的な結果を出力Write-Output
ユーザーへの状況報告Write-Host
詳細な処理情報Write-Verbose
開発時のデバッグWrite-Debug
警告メッセージWrite-Warning
エラーメッセージWrite-Error

学習のステップ

  1. 基本的な使い方:Write-Debugと[CmdletBinding()]を覚える
  2. 表示制御:-Debugスイッチと$DebugPreferenceをマスター
  3. 実践活用:変数確認、条件分岐、ループで使ってみる
  4. 高度な技術:構造化メッセージ、条件付きデバッグを試す

コメント

タイトルとURLをコピーしました