コンテンツにスキップ

PowerShellの解析モード(Parsing Mode)について

[質問]

入門書の説明で、あえて「Write-Outputで 1..10を出力する」というコマンド例を書くとき、「Write-Output 1..10」だと引数が式として解釈されません。「Write-Output (1..10)」ならうまくいきます。

[回答] Gemini

おっしゃる通り、PowerShellの「解析モード(Parsing Mode)」の違いによる落とし穴ですね。

Write-Output 1..10 と書くと、PowerShellはこれを数値の範囲ではなく、「1..10」という1つの文字列(正確には引数モードでの未引用文字列)として解釈しようとしてしまいます。

これを正しく実行させるための3つの書き方の違いを整理しました。


1. Write-Output (1..10) (グループ化演算子)

最も推奨される、標準的な書き方です。

  • 仕組み: ()グループ化演算子です。括弧の中身を「一つの式」として先に評価し、その結果(配列)Write-Output の引数に渡します。
  • メリット: 簡潔で、算術演算や範囲演算を引数に渡す際のデファクトスタンダードです。

2. Write-Output $(1..10) (サブ式)

「処理を完結させてから渡す」というニュアンスが強くなります。

  • 仕組み: $() は中身を独立したスクリプトブロックのように実行します。
  • 違い: () との結果は同じですが、$() の中には ;(セミコロン)を使って複数の命令を書くことができます(例: $( $a=1; $a..10 ))。
  • 使い所: 単なる範囲指定だけならオーバースペックですが、代入や計算を複数行わずに行いたい場合に便利です。

3. Write-Output @(1..10) (配列サブ式)

「結果を確実に配列として扱いたい」という意思表示です。

  • 仕組み: 中身を評価したあと、結果が単一のオブジェクトであっても強制的に [Object[]](配列)に包み直します。
  • 今回のケース: 1..10 は元々配列を返すため、結果的に () と同じ挙動になります。
  • 使い所: 出力結果が1つなのか複数なのか不明なコマンドの結果を、安全にループ処理(foreach)に流したい時に多用されます。

比較まとめ

構文 読み方 主な役割 入門書でのニュアンス
(...) グループ化 式を優先的に評価する 「この計算結果を引数にしてね」
$(...) サブ式 命令を実行し結果を返す 「一連の処理の結果をここに置いてね」
@(...) 配列サブ式 常に配列として返す 「何が何でも配列として扱ってね」

補足:なぜ 1..10 だけだとダメなのか?

PowerShellには2つのモードがあります。

  1. 式モード(Expression Mode): 数値計算や変数の代入を行うモード。1..10 は配列として解釈される。
  2. 引数モード(Argument Mode): コマンドのあとに続くモード。ここでは 1..10 は「ただの文字列」として扱われがち。

Write-Output はコマンドなので、その後の引数は「引数モード」で解析されます。そのため、「ここからは式モードで計算してね」と合図を送るために () などの記号が必要になるわけです。

次は、この「モードの違い」が原因でよく起こる別のエラー(パス指定や計算式など)についても解説しましょうか?


「モードの違い」が原因でよく起こる別のエラーの例を解説してください

PowerShellの「モードの違い」は、初心者から中級者までが最も頻繁にハマるポイントです。

PowerShellは、「式モード(計算機のモード)」「引数モード(シェル・コマンドのモード)」を、文脈に応じて自動で切り替えています。この切り替えが意図せずに行われることでエラーや予期せぬ挙動が発生します。

よくあるエラー例を3つ挙げます。


1. パス指定や文字列結合での計算エラー

もっとも多いのが、コマンドの引数に「計算結果」をそのまま渡そうとして失敗するパターンです。

  • 失敗例: 1 + 2 の結果(3)を名前に含むディレクトリを作りたい。

    # 期待: "Folder3" を作成
    New-Item -Path Folder + (1 + 2) -ItemType Directory
    

  • なぜエラーか: New-Item(コマンド)の後は引数モードです。ここでは +( も「ただの文字」として扱われようとします。結果、「Folderという名前のフォルダ」と「+という名前のフォルダ」を作ろうとするなど、カオスな挙動になります。

  • 正解: () で囲んで、強制的にその部分だけ式モードにします。
    New-Item -Path ("Folder" + (1 + 2)) -ItemType Directory
    

2. プロパティ参照(ドット)の空振り

オブジェクトの特定のプロパティを表示しようとして、プロパティ名が「ただの文字列」として出力されるパターンです。

  • 失敗例: 今日の日付の「年」だけを表示したい。

    Write-Output Get-Date.Year
    

  • なぜエラーか: 引数モードでは .(ドット)は特別な意味を持たず、Get-Date.Year という一続きの「文字列」として解釈されます。

  • 正解: () で囲むことで、まず「日付オブジェクトを取得」し、その「Yearプロパティにアクセスする」という式モードの動きを保証します。
    Write-Output (Get-Date).Year
    

3. 文字列内での変数展開(プロパティの場合)

ダブルクォートの中で変数のプロパティを表示しようとする際のエラーです。

  • 失敗例:

    $proc = Get-Process -Id $PID
    Write-Output "実行中のIDは $proc.Id です"
    # 出力: 実行中のIDは System.Diagnostics.Process (pwsh).Id です
    

  • なぜエラーか: 文字列展開(引数モードの一部)では、$ の直後の変数名($proc)までは認識しますが、その後の .Id は「ただの地文」として扱われます。

  • 正解: ここで **サブ式 $()** の出番です。
    Write-Output "実行中のIDは $($proc.Id) です"
    

まとめ:モードの見極め方

PowerShellがどちらのモードで動いているかは、「行の最初が何か」で決まります。

始まりの文字 モード 解釈のルール
英単語 (Get-, cd 等) 引数モード スペース区切りで「文字」として扱う。
記号・数値 ($, (, 1, ") 式モード 変数や計算、文字列オブジェクトとして扱う。

💡 迷った時の鉄則

「コマンドの途中で計算やプロパティ参照をしたいなら、とりあえず ()$() で囲む」

これだけで、PowerShellにおけるエラーの8割は回避できます。

次に、これに関連して「文字列の中で複雑な計算を埋め込むテクニック」について詳しく知りたいですか?