about_Parsing

簡単な説明

PowerShell でコマンドを解析する方法について説明します。

詳細な説明

コマンド プロンプトでコマンドを入力すると、PowerShell はコマンド テキストを tokens と呼ばれる一連のセグメントに分割し 各トークンの解釈方法を決定します。

たとえば、次のように入力します。

Write-Host book

PowerShell は、コマンドを Write-Hostbook の 2 つのトークンに分割し、式モードと引数モードの 2 つの主要な解析モードのいずれかを使用して、各トークンを個別に解釈します。

Note

PowerShell はコマンド入力を解析するときに、コマンド名をコマンドレットまたはネイティブ実行可能ファイルに解決しようとします。 コマンド名が完全に一致しない場合、PowerShell は既定の動詞としてコマンドの先頭に Get- します。 たとえば、PowerShell は ServiceGet-Serviceとして解析します。 次の理由から、この機能を使用することはお勧めしません。

  • 非効率的です。 これにより、PowerShell が複数回検索されます。
  • 同じ名前の外部プログラムが最初に解決されるため、目的のコマンドレットを実行することはできません。
  • Get-HelpGet-Command は動詞のない名前を認識しません。
  • コマンド名には、予約語または言語キーワードを指定できます。 Process は両方であり、 Get-Processに解決することはできません。

式モード

式モードは、スクリプト言語での値操作に必要な式を組み合わせることを目的としています。 式は PowerShell 構文での値の表現であり、単純または複合にすることができます。次に例を示します。

リテラル式は、その値を直接表現します。

'hello'
32

変数式は、参照する変数の値を保持します。

$x
$script:path

演算子は、評価のために他の式を結合します。

-12
-not $Quiet
3 + 7
$input.Length -gt 1
  • 文字列リテラル は引用符で囲む必要があります。
  • 数値 は、一連の文字としてではなく数値として扱われます (エスケープされない限り)。
  • 演算子--notなどの単項演算子、+-gtなどの二項演算子を含め、演算子として解釈され、それぞれの演算が引数 (オペランド) に適用されます。
  • 属性式と変換式 は式として解析され、下位式に適用されます。 (例: [int] '7')。
  • 変数参照 は値に対して評価されますが、 splatting は禁止され、パーサー エラーが発生します。
  • それ以外は、呼び出されるコマンドとして扱われます。

引数モード

解析時に、PowerShell はまず入力を式として解釈します。 ただし、コマンド呼び出しが発生すると、解析は引数モードで続行されます。 パスなどのスペースを含む引数がある場合は、それらの引数値を引用符で囲む必要があります。

引数モードは、シェル環境のコマンドの引数とパラメーターを解析するために設計されています。 次のいずれかの構文を使用しない限り、すべての入力は展開可能な文字列として扱われます。

  • ドル記号 ($) の後に変数名が続く変数参照が開始されます。それ以外の場合は、展開可能な文字列の一部として解釈されます。 変数参照には、メンバー アクセスまたはインデックス作成を含めることができます。

    • $HOMEなどの単純な変数参照に続く追加の文字は、同じ引数の一部と見なされます。 変数名を中かっこ ({}) で囲んで、後続の文字から区切ります。 たとえば、${HOME} のようにします。
    • 変数参照にメンバー アクセスが含まれている場合、最初の追加文字は新しい引数の先頭と見なされます。 たとえば、 $HOME.Length-more は、 $HOME.Length の値と文字列リテラルの -moreという 2 つの引数になります。
  • 引用符 ('") の開始文字列

  • 中かっこ ({}) が新しいスクリプト ブロックを開始する

  • コンマ (,) は、呼び出されるコマンドがネイティブ アプリケーションでない限り、配列として渡されるリストを導入します。その場合は、展開可能な文字列の一部として解釈されます。 最初、連続、または末尾のコンマはサポートされていません。

  • かっこ (()) で新しい式を開始する

  • 部分式演算子 ($()) が埋め込み式を開始します

  • 最初の記号 (@) は、スプラッティング (@args)、配列 (@(1,2,3))、ハッシュ テーブル リテラル (@{a=1;b=2}) などの式構文を開始します。

  • ()$()、およびトークンの開始時に @() は、式または入れ子になったコマンドを含むことができる新しい解析コンテキストを作成します。

    • 後に追加の文字が続くと、最初の追加文字は新しい個別の引数の先頭と見なされます。
    • 引用符で囲まれていないリテラル $() が展開可能な文字列のように動作する場合、 () は式である新しい引数を開始し、 @() はリテラル @ として受け取られ、 () 式の新しい引数が開始されます。
  • それ以外はすべて、エスケープが必要なメタ文字を除き、展開可能な文字列として扱われます。 特殊文字の処理に関するを参照してください。

    • 引数モードメタ文字 (特殊な構文の意味を持つ文字) は、 <space> ' " ` , ; ( ) { } | & < > @ #です。 これらのうち、 < > @ # はトークンの開始時にのみ特別です。
  • 停止解析トークン (--%) は、残りのすべての引数の解釈を変更します。 詳細については、後述の「 ストップ解析トークン 」セクションを参照してください。

次の表は、式モードと引数モードで処理されるトークンの例と、それらのトークンの評価を示しています。 これらの例では、変数 $a の値は 4

モード 結果
2 Expression 2 (整数)
`2 Expression "2" (コマンド)
Write-Output 2 Expression 2 (整数)
2+2 Expression 4 (整数)
Write-Output 2+2 引数 "2+2" (文字列)
Write-Output(2+2) Expression 4 (整数)
$a Expression 4 (整数)
Write-Output $a Expression 4 (整数)
$a+2 Expression 6 (整数)
Write-Output $a+2 引数 "4+2" (文字列)
$- 引数 "$-" (コマンド)
Write-Output $- 引数 "$-" (文字列)
a$a Expression "a$a" (コマンド)
Write-Output a$a 引数 "a4" (文字列)
a'$a' Expression "a$a" (コマンド)
Write-Output a'$a' 引数 "a$a" (文字列)
a"$a" Expression "a$a" (コマンド)
Write-Output a"$a" 引数 "a4" (文字列)
a$(2) Expression "a$(2)" (コマンド)
Write-Output a$(2) 引数 "a2" (文字列)

すべてのトークンは、 Boolean または String など、何らかの種類のオブジェクト型として解釈できます。 PowerShell は、式からオブジェクトの種類を決定しようとします。 オブジェクトの種類は、コマンドで想定されるパラメーターの種類と、引数を正しい型に変換する方法を PowerShell が認識しているかどうかによって異なります。 次の表に、式によって返される値に割り当てられた型の例をいくつか示します。

モード 結果
Write-Output !1 argument "!1" (文字列)
Write-Output (!1) 式 (expression) False (ブール値)
Write-Output (2) 式 (expression) 2 (整数)
Set-Variable AB A,B argument 'A','B' (配列)
CMD /CECHO A,B argument 'A,B' (文字列)
CMD /CECHO $AB 式 (expression) 'A B' (配列)
CMD /CECHO :$AB argument ':A B' (文字列)

特殊文字の処理

バックティック文字 (`) を使用して、式内の特殊文字をエスケープできます。 これは、メタ文字としてではなくリテラル文字として使用する引数モードのメタ文字をエスケープする場合に最も便利です。 たとえば、ドル記号 ($) を展開可能な文字列のリテラルとして使用するには、次のようにします。

"The value of `$ErrorActionPreference is '$ErrorActionPreference'."
The value of $ErrorActionPreference is 'Continue'.

行の連結

行の末尾にバックティック文字を使用して、次の行で入力を続行することもできます。 これにより、長い名前と引数の値を持つ複数のパラメーターを受け取るコマンドの読みやすさが向上します。 次に例を示します。

New-AzVm `
    -ResourceGroupName "myResourceGroupVM" `
    -Name "myVM" `
    -Location "EastUS" `
    -VirtualNetworkName "myVnet" `
    -SubnetName "mySubnet" `
    -SecurityGroupName "myNetworkSecurityGroup" `
    -PublicIpAddressName "myPublicIpAddress" `
    -Credential $cred

ただし、行連結の使用は避ける必要があります。

  • バックティック文字は見にくく、忘れやすい場合があります。
  • バックティックの後に余分なスペースが追加され、行の継続が中断されます。 スペースが見えにくいので、エラーを見つけるのが難しい場合があります。

PowerShell には、構文の自然なポイントで改行する方法がいくつか用意されています。

  • パイプ文字の後 (|)
  • 二項演算子 (+--eqなど) の後
  • 配列内のコンマ (,) の後
  • [{などの文字を開いた後、(

大きなパラメーター セットの場合は、代わりにスプラッティングを使用してください。 次に例を示します。

$parameters = @{
    ResourceGroupName = "myResourceGroupVM"
    Name = "myVM"
    Location = "EastUS"
    VirtualNetworkName = "myVnet"
    SubnetName = "mySubnet"
    SecurityGroupName = "myNetworkSecurityGroup"
    PublicIpAddressName = "myPublicIpAddress"
    Credential = $cred
}
New-AzVm @parameters

ネイティブ コマンドに引数を渡す

PowerShell からネイティブ コマンドを実行する場合、引数は最初に PowerShell によって解析されます。 解析された引数は、各パラメーターがスペースで区切られた 1 つの文字列に結合されます。

たとえば、次のコマンドは、 icacls.exe プログラムを呼び出します。

icacls X:\VMS /grant Dom\HVAdmin:(CI)(OI)F

PowerShell 2.0 でこのコマンドを実行するには、エスケープ文字を使用して PowerShell がかっこを誤って解釈しないようにする必要があります。

icacls X:\VMS /grant Dom\HVAdmin:`(CI`)`(OI`)F

停止解析トークン

PowerShell 3.0 以降では、 ストップ解析 (--%) トークンを使用して、PowerShell が入力を PowerShell コマンドまたは式として解釈するのを停止できます。

Note

停止解析トークンは、Windows プラットフォームでのネイティブ コマンドの使用のみを目的としています。

ネイティブ コマンドを呼び出すときは、プログラム引数の前に解析停止トークンを配置します。 この手法は、誤った解釈を防ぐためにエスケープ文字を使用するよりもはるかに簡単です。

停止解析トークンが検出されると、PowerShell は行の残りの文字をリテラルとして扱います。 実行する唯一の解釈は、 %USERPROFILE%など、標準の Windows 表記を使用する環境変数の値を置き換えます。

icacls X:\VMS --% /grant Dom\HVAdmin:(CI)(OI)F

PowerShell は、次のコマンド文字列を icacls.exe プログラムに送信します。

X:\VMS /grant Dom\HVAdmin:(CI)(OI)F

停止解析トークンは、次の改行文字またはパイプライン文字まで有効です。 行連結文字 (`) を使用してその効果を拡張したり、コマンド区切り記号 (;) を使用してその効果を終了したりすることはできません。

環境変数参照 %variable% 以外に、コマンドに他の動的要素を埋め込むことはありません。 %文字を%%としてエスケープすることは、バッチ ファイル内で実行できる方法ではサポートされていません。 %<name>% トークンは常に拡張されます。 <name>が定義された環境変数を参照していない場合、トークンはそのまま渡されます。

ストリーム リダイレクト ( >file.txt など) は、ターゲット コマンドに引数として逐語的に渡されるため、使用できません。

次の例では、最初の手順では、停止解析トークンを使用せずにコマンドを実行します。 PowerShell は引用符で囲まれた文字列を評価し、値 (引用符なし) を cmd.exeに渡します。その結果、エラーが発生します。

PS> cmd /c echo "a|b"
'b' is not recognized as an internal or external command,
operable program or batch file.
PS> cmd /c --% echo "a|b"
"a|b"

Note

PowerShell コマンドレットを使用する場合、停止解析トークンは必要ありません。 ただし、これらの引数を使用してネイティブ コマンドを呼び出すために設計された PowerShell 関数に引数を渡すと便利な場合があります。

引用符文字を含む引数を渡す

一部のネイティブ コマンドでは、引用符文字を含む引数が必要です。 PowerShell 7.3 では、ネイティブ コマンドのコマンド ラインの解析方法が変更されました。

注意事項

新しい動作は、Windows PowerShell 5.1 の動作の大きな変更です。 これにより、ネイティブ アプリケーションを呼び出す際のさまざまな問題に対処するスクリプトと自動化が中断される場合があります。 必要に応じネイティブ引数の受け渡しを回避するには、停止解析トークン (--%) または Start-Process コマンドレットを使用します。

この動作は、新しい $PSNativeCommandArgumentPassing ユーザー設定変数によって制御されます。 この変数により、ランタイム時の動作を選択することができます。 有効な値は、LegacyStandardWindows です。 既定の動作は、プラットフォームに固有です。 Windows プラットフォームの場合、既定の設定は Windows で、Windows 以外のプラットフォームでは既定で Standard です。

Legacy は過去の動作です。 Windows および Standard モードの動作は同じですが、Windows モードでは、次のファイルの呼び出しで Legacy スタイル引数の受け渡しが自動的に使用されます。

  • cmd.exe
  • cscript.exe
  • wscript.exe
  • .bat で終わる
  • .cmd で終わる
  • .js で終わる
  • .vbs で終わる
  • .wsf で終わる

$PSNativeCommandArgumentPassingLegacy または Standard のいずれかに設定されている場合、パーサーはこれらのファイルのチェックは行いません。

Note

次の例では、TestExe.exe ツールを使用します。 ソース コードから TestExe をビルドすることができます。 PowerShell ソース リポジトリの TestExe に関するページを参照してください。

この変更によって利用可能になった新しい動作は次のとおりです。

  • リテラルまたは展開可能な文字列に引用符が埋め込まれ、その引用符が保持されるようになりました。

    PS> $a = 'a" "b'
    PS> TestExe -echoargs $a 'c" "d' e" "f
    Arg 0 is <a" "b>
    Arg 1 is <c" "d>
    Arg 2 is <e f>
    
  • 空の文字列が引数として保持されるようになりました。

    PS> TestExe -echoargs '' a b ''
    Arg 0 is <>
    Arg 1 is <a>
    Arg 2 is <b>
    Arg 3 is <>
    

これらの例の目的は、パスを引用符で囲まれた文字列として受け取ることができるように、ネイティブ コマンドに "C:\Program Files (x86)\Microsoft\" ディレクトリ パス (スペースと引用符付き) を渡すことです。

Windows モードまたはStandard モードでは、次の例で期待される結果が生成されます。

TestExe -echoargs """${env:ProgramFiles(x86)}\Microsoft\"""
TestExe -echoargs '"C:\Program Files (x86)\Microsoft\"'

Legacy モードで同じ結果を取得するには、引用符をエスケープするか、停止解析トークン (--%) を使用する必要があります。

TestExe -echoargs """""${env:ProgramFiles(x86)}\Microsoft\\"""""
TestExe -echoargs "\""C:\Program Files (x86)\Microsoft\\"""
TestExe -echoargs --% ""\""C:\Program Files (x86)\Microsoft\\"\"""
TestExe -echoargs --% """C:\Program Files (x86)\Microsoft\\""
TestExe -echoargs --% """%ProgramFiles(x86)%\Microsoft\\""

Note

バックスラッシュ (\) 文字は、PowerShell ではエスケープ文字として認識されません。 ProcessStartInfo.ArgumentList の基になる API によって使用されるエスケープ文字です。

PowerShell 7.3 では、ネイティブ コマンドのパラメーター バインドをトレースする機能も追加されました。 詳しくは、「トレース-コマンド」をご覧ください。

PowerShell コマンドに引数を渡す

PowerShell 3.0 以降では、 end-of-parameters トークン (--) を使用して、PowerShell が入力を PowerShell パラメーターとして解釈するのを停止できます。 これは POSIX シェルおよびユーティリティー仕様で指定されている規則です。

パラメーターの終了トークン

パラメーターの終了トークン (--) は、それに続くすべての引数が、その周囲に二重引用符が配置されたかのように実際の形式で渡されることを示します。 たとえば、 -- を使用すると、引用符を使用したり、パラメーターとして解釈したりせずに、文字列 -InputObject を出力できます。

Write-Output -- -InputObject
-InputObject

停止解析 (--%) トークンとは異なり、 -- トークンに続く値は、PowerShell によって式として解釈できます。

Write-Output -- -InputObject $env:PROCESSOR_ARCHITECTURE
-InputObject
AMD64

この動作は、PowerShell コマンドにのみ適用されます。 外部コマンドを呼び出すときに -- トークンを使用すると、 -- 文字列がそのコマンドに引数として渡されます。

TestExe -echoargs -a -b -- -c

出力は、 -- が引数として TestExeに渡されることを示しています。

Arg 0 is <-a>
Arg 1 is <-b>
Arg 2 is <-->
Arg 3 is <-c>

チルダ (~)

チルダ文字 (~) は、PowerShell で特別な意味を持ちます。 パスの先頭にある PowerShell コマンドで使用すると、チルダ文字がユーザーのホーム ディレクトリに展開されます。 チルダ文字がパス内の他の場所で使用されている場合は、リテラル文字として扱われます。

PS D:\temp> $PWD

Path
----
D:\temp

PS D:\temp> Set-Location ~
PS C:\Users\user2> $PWD

Path
----
C:\Users\user2

この例では、New-ItemName パラメーターに文字列が必要です。 チルダ文字はリテラル文字として扱われます。 新しく作成されたディレクトリに変更するには、チルダ文字でパスを修飾する必要があります。

PS D:\temp> Set-Location ~
PS C:\Users\user2> New-Item -Type Directory -Name ~

    Directory: C:\Users\user2

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----            5/6/2024  2:08 PM                ~

PS C:\Users\user2> Set-Location ~
PS C:\Users\user2> Set-Location .\~
PS C:\Users\user2\~> $PWD

Path
----
C:\Users\user2\~

ネイティブ コマンドでチルダ文字を使用すると、PowerShell はチルダをリテラル文字として渡します。 パスでチルダを使用すると、チルダ文字をサポートしていない Windows のネイティブ コマンドでエラーが発生します。

PS D:\temp> $PWD

Path
----
D:\temp

PS D:\temp> Get-Item ~\repocache.clixml

    Directory: C:\Users\user2

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           4/29/2024  3:42 PM          88177 repocache.clixml

PS D:\temp> more.com ~\repocache.clixml
Cannot access file D:\temp\~\repocache.clixml

関連項目