PowerShell の ErrorAction で業務SE が踏む3パターンの落とし穴 — Continue/Stop/SilentlyContinue と try-catch

みなさんこんにちは!ヒロポンです!

PowerShell で try-catch を書いたのに、 エラーが catch されずに後続コードが普通に実行された ことはないですか??

try {
    Get-ChildItem -Path '/notexist'
    Write-Host "ここが何故か実行される"
} catch {
    Write-Host "なぜここに来ない?"
}

俺もこれで 30分ハマって「なんで catch 入らないの…??」 とコードを睨み続けた 記憶があります。 PowerShell 初心者あるあるで、 C# の例外処理感覚で書くと普通に詰まる場所。

正体は Non-terminating エラー という PowerShell 固有の概念。 で、 これを制御するのが ErrorAction という cmdlet パラメータです。

今回は ErrorAction の3パターン (Continue / Stop / SilentlyContinue) の挙動を、 Docker で実走したターミナル出力つきで解説します。

目次

TL;DR

  • PowerShell のエラーは TerminatingNon-terminating の2種類で、 多くの cmdlet エラーは Non-terminating
  • try-catch が効くのは Terminating だけ。 -ErrorAction Stop を付けて Non-terminating を Terminating に変換するのが正解
  • 業務SE が押さえる ErrorAction は 3つ: Continue (デフォルト・catch効かない) / Stop (catch効く) / SilentlyContinue (エラー抑制)
  • ハマりポイントは3つ: $ErrorActionPreference 全域設定の副作用 / SilentlyContinue 乱用 / catch 内で $_ を見落とす
  • Docker (verify-pwsh container) 検証は PowerShell 言語仕様レベルのみ。 Active Directory や Exchange など モジュール固有 cmdlet は Windows 実機環境 で別途確認してください

原因 — Non-terminating エラーの正体

C# / Java 出身者が PowerShell でハマる最大の落とし穴がこれ。

PowerShell のエラーには 2種類 あります。

種類 try-catch 挙動
Terminating 構文エラー / throw / .NET 例外 ◎ catch される 実行が即停止
Non-terminating Get-ChildItem の Not Found / Write-Error × catch されない エラー出力後に続行

問題なのは、 業務系で使う cmdlet の大半は Non-terminating エラーを返す こと。 ファイルが無い、 ユーザーが無い、 ネットワーク到達不可、 全部 Non-terminating。

つまり「try-catch で安心」 と書いたつもりが、 エラー出力が赤字で出るだけで後続コードがそのまま実行される。 これが業務系で一番怖い挙動です。

ハンズオン #1 — Continue (デフォルト) で catch されない

実際にやってみます。 Write-Error で意図的に Non-terminating エラーを出して、 try-catch がどう動くか確認。

Write-Host "=== Block 1: ErrorAction Continue (デフォルト) ==="
Write-Host "[1] try ブロックの直前"

try {
    Write-Error "ファイルが見つかりません: /notexist-12345"
    Write-Host "[2] Write-Error の直後 — Continue だとここが実行される"
} catch {
    Write-Host "[CATCH] 捕捉メッセージ: $($_.Exception.Message)"
}

Write-Host "[3] スクリプト末尾"

verify-pwsh container で実走した結果がこちら。

ErrorAction Continue で try-catch が効かず後続実行される様子

注目ポイントは3つ。

  • [2]実行されている → エラーが出ても後続コードが走った
  • [CATCH]出ていない → try-catch が機能していない
  • 赤字で Write-Error: ファイルが見つかりません → エラー自体は stderr に出力されている

これが Non-terminating エラーの典型挙動。 「エラーは出るけど例外じゃない」 という、 C# / Java 出身者の直感を裏切るやつです。

ちなみに Get-ChildItem -Path '/notexist' でも同じ挙動になります (cmdlet 内部で Write-Error を呼んでいるため)。 業務系で多用する Invoke-WebRequest Get-ADUser Test-Connection も同類。

ハンズオン #2 — Stop を付けると catch が効く

同じコードに -ErrorAction Stop を1つ追加するだけで挙動が変わります。

Write-Host "=== Block 2: ErrorAction Stop ==="
Write-Host "[1] try ブロックの直前"

try {
    Write-Error "ファイルが見つかりません: /notexist-12345" -ErrorAction Stop
    Write-Host "[2] この行は実行されない (Stop で例外発生)"
} catch {
    Write-Host "[CATCH] 捕捉メッセージ: $($_.Exception.Message)"
}

Write-Host "[3] スクリプト末尾"

実行結果。

ErrorAction Stop で catch ブロックに飛ぶ挙動

今度は3点ぜんぶ逆転。

  • [2]出ていない → try ブロックは Write-Error の時点で中断
  • [CATCH]メッセージが捕捉 された → catch ブロックが実行された
  • stderr が → catch で握ったので error stream に流れていない

これが正解の挙動。 -ErrorAction Stop で Non-terminating を Terminating に変換 することで、 ようやく try-catch が いい感じに機能します。

3 ErrorAction の比較表 — 3軸で違いを並べる

ErrorAction には全部で6種類ありますが、 業務SE が押さえるのは 3パターン で十分。 try-catch 連携 / 再実行可能性 / ログ残し方の3軸でまとめたのが下の表です。

PowerShell ErrorAction 3パターン比較表 (try-catch 連携 / 再実行可能性 / ログ残し方)

要点:

  • Stop — try-catch 効く・catch 内で構造化ログ出せる・冪等性確保 → 業務系の止めるべき処理に最適
  • Continue (デフォルト) — try-catch 効かない・赤字エラーは出る → 通知だけで済む処理向け
  • SilentlyContinue — エラー痕跡ゼロ・失敗に気付けない → 想定済みエラーの抑制のみ

Ignore / Inquire / Suspend もありますが、 業務SE が日常使いするのはこの3つだけです。

ハマりポイント3つ (現場で踏みがちなやつ)

ここまで「ErrorAction Stop を付ければ大丈夫」 みたいに書いてきましたが、 そうじゃないケースが3つあります

$ErrorActionPreference = 'Stop' の副作用

スクリプト全体に Stop を効かせるなら、 先頭にこう書けば一発です。

$ErrorActionPreference = 'Stop'

ただしこれ、 想定外の cmdlet まで Stop になる。 俺もある日、 ログイン履歴チェック用のスクリプトに $ErrorActionPreference = 'Stop' を仕込んだら、 Test-Connection のタイムアウトでスクリプト全体が落ちて、 朝1時間原因に辿り着けなかった経験があります。

対策は try-catch で囲んだ範囲だけ Stop にする か、 cmdlet 単位で -ErrorAction Stop を付ける。 グローバル設定は使う場面を絞るのが安全。

② SilentlyContinue 乱用の弊害

「エラーが赤字で出るのが鬱陶しい」 という理由で -ErrorAction SilentlyContinue を多用すると、 本当に止めるべきエラーまで握り潰す ことになります。

俺もこれで、 ある夜間バッチで Copy-Item -ErrorAction SilentlyContinue を使ってたら、 コピー先のディスクが full で 3日分のファイルが消失 していたのを翌朝まで把握できなかった経験があります。 朝出社して気付いて肝が冷えました。

対策は SilentlyContinue を 想定済みのエラー (例: ファイル存在チェックで無くてもOK な場面) のみ に絞る。 デフォルトは Continue (赤字で出るほう) のままにしておく。

③ catch 内で $_ を見落とす

catch ブロックは入ってる前提だけど、 どんなエラーかをログに残さない と後でデバッグできない。

# ✗ 中身ゼロ
} catch {
    Write-Host "エラーが発生しました"
}

# ✓ $_ でエラー情報を取り出す
} catch {
    Write-Host "エラー: $($_.Exception.Message)"
    Write-Host "発生位置: $($_.InvocationInfo.PositionMessage)"
    Write-Host "スタックトレース: $($_.ScriptStackTrace)"
}

これは去年、 後輩が書いたスクリプトのデバッグを2時間やった時に「$_ 見てないせいで原因が一切分からん」 という事態に踏み込んだ経験から。 catch 句は $_ の中身をログに残す のが鉄則。

まとめ

PowerShell のエラー処理は C# / Java の直感が通用しない 最大の落とし穴領域。 ポイントを3つだけ:

  • try-catch を書く時は -ErrorAction Stop もセット で書く
  • $ErrorActionPreference = 'Stop' はスクリプト先頭で全体適用できるが副作用に注意
  • SilentlyContinue は想定済みエラーのみ に絞り込む・乱用しない

これで PowerShell エラー処理の地雷踏みは いい感じに 9割回避できます!!

ぶっちゃけ、 業務SE が PowerShell に手を出した時の 「なんで try-catch 効かないんだ??」 は こんな感じで全員一度は通る道です!! ここを早期に踏んでおくと、 後輩が同じところで悩んでる時に 「ああそれ ErrorAction Stop 付けてないだろ」 と即座に救えるようになるので、 結構美味しいスキルです。

よくある質問

Q1. try-catch を書いたのに catch ブロックが実行されないのはなぜですか?

A. PowerShell の多くの cmdlet が出すのは Non-terminating エラー で、 デフォルトの ErrorAction Continue では try-catch では捕捉できません。 -ErrorAction Stop を付けると Terminating に変換されて catch が効きます。 これが PowerShell 初心者あるある№1です。

Q2. ErrorAction を全体に効かせるには?

A. スクリプト先頭に $ErrorActionPreference = 'Stop' を書くと、 その配下の cmdlet 全てに ErrorAction Stop が適用されます。 ただしハマりポイント①で書いた通り、 想定外の cmdlet までStop になる副作用 があるので、 使う場面は絞るのが安全。

Q3. SilentlyContinue はどんな時に使う?

A. 想定済みの軽微なエラー (例: ファイル存在チェックで無くてもOK・テンポラリ削除で既に消えてた場面) を抑制したい時だけ。 乱用すると重大なエラーまで無視して、 デバッグ困難になります。 デフォルトは Continue (赤字で出る方) のままにしておくのが推奨です。

関連記事

以上!


この記事が気に入ったら
いいねしてね!

どんどんシェア待ってるぜ!!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次