簡単な説明
PowerShell でコマンドを解析する方法について説明します。
長い説明
コマンド プロンプトでコマンドを入力すると、PowerShell はコマンド テキストを トークン と呼ばれる一連のセグメントに分割し、各トークンの解釈方法を決定します。
たとえば、次のように入力します。
Write-Host book
PowerShell は、コマンドを Write-Host と bookの 2 つのトークンに分割し、式モードと引数モードの 2 つの主要な解析モードのいずれかを使用して、各トークンを個別に解釈します。
手記
PowerShell はコマンド入力を解析するときに、コマンド名をコマンドレットまたはネイティブ実行可能ファイルに解決しようとします。 コマンド名が完全に一致しない場合、PowerShell は既定の動詞としてコマンドの先頭に Get- します。 たとえば、PowerShell は Service を Get-Serviceとして解析します。 次の理由から、この機能を使用することはお勧めしません。
- 非効率的です。 これにより、PowerShell が複数回検索されます。
- 同じ名前の外部プログラムが最初に解決されるため、目的のコマンドレットを実行することはできません。
-
Get-HelpとGet-Commandは動詞のない名前を認識しません。 - コマンド名には、予約語または言語キーワードを指定できます。
Processは両方であり、Get-Processに解決することはできません。
式モード
式モードは、スクリプト言語での値操作に必要な式を組み合わせることを目的としています。 式は PowerShell 構文での値の表現であり、単純または複合にすることができます。次に例を示します。
リテラル式は、その値を直接表現します。
'hello'
32
変数式は、参照する変数の値を保持します。
$x
$Script:path
演算子は、評価のために他の式を結合します。
-12
-not $Quiet
3 + 7
$input.Length -gt 1
- 文字列リテラル は二重引用符で囲む必要があります。
- 数値 は、(エスケープされない限り) 一連の文字ではなく数値として扱われます。
-
演算子(
-、-notなどの単項演算子、+や-gtなどの二項演算子を含む) は演算子として解釈され、それぞれの演算が引数 (オペランド) に適用されます。 -
属性式と変換式 式として解析され、下位式に適用されます。 例:
[int] '7'. - 変数参照 は値に対して評価されますが、スプラッティング は禁止され、パーサー エラーが発生します。
- それ以外は、呼び出されるコマンドとして扱われます。
引数モード
解析時に、PowerShell はまず入力を式として解釈します。 ただし、コマンド呼び出しが発生すると、解析は引数モードで続行されます。 パスなどのスペースを含む引数がある場合は、それらの引数値を引用符で囲む必要があります。
引数モードは、シェル環境のコマンドの引数とパラメーターを解析するために設計されています。 次のいずれかの構文を使用しない限り、すべての入力は展開可能な文字列として扱われます。
ドル記号 (
$) の後に変数名が続き、変数参照が開始されます。それ以外の場合は、展開可能な文字列の一部として解釈されます。 変数参照には、メンバー アクセスまたはインデックス作成を含めることができます。-
$HOMEなどの単純な変数参照に続く追加の文字は、同じ引数の一部と見なされます。 変数名を中かっこ ({}) で囲んで、後続の文字から区切ります。 たとえば、${HOME}します。 - 変数参照にメンバー アクセスが含まれている場合、最初の追加文字は新しい引数の先頭と見なされます。 たとえば、
$HOME.Length-moreは、$HOME.Lengthの値と文字列リテラルの-moreという 2 つの引数になります。
-
引用符 (
'および") が文字列を開始する中かっこ (
{}) が新しいスクリプト ブロックを開始するコンマ (
,) は、呼び出されるコマンドがネイティブ アプリケーションでない限り、配列として渡されるリストを導入します。その場合は、展開可能な文字列の一部として解釈されます。 最初、連続、または末尾のコンマはサポートされていません。括弧 (
()) は新しい式を開始するために使われる部分式演算子 (
$()) は、埋め込み式を開始する最初の記号 (
@) は、スプラッティング (@args)、配列 (@(1,2,3))、ハッシュ テーブル リテラル (@{a=1;b=2}) などの式構文を開始します。トークンの開始時に
()、$()、および@()は、式または入れ子になったコマンドを含むことができる新しい解析コンテキストを作成します。- 後に追加の文字が続くと、最初の追加文字は新しい個別の引数の先頭と見なされます。
- 引用符で囲まれていないリテラル
$()が展開可能な文字列のように動作する場合、()は式である新しい引数を開始し、@()はリテラル@として受け取られ、()式の新しい引数が開始されます。
それ以外はすべて、エスケープが必要なメタ文字を除き、展開可能な文字列として扱われます。 特殊文字の処理
を参照してください。 - 引数モードメタ文字 (特殊な構文の意味を持つ文字) は、
<space> ' " ` , ; ( ) { } | & < > @ #です。 これらのうち、< > @ #はトークンの開始時にのみ特別です。
- 引数モードメタ文字 (特殊な構文の意味を持つ文字) は、
停止解析トークン (
--%) は、残りのすべての引数の解釈を変更します。 詳細については、後述の「stop-parsing token」セクションを参照してください。
使用例
次の表は、式モードと引数モードで処理されるトークンの例と、それらのトークンの評価を示しています。 これらの例では、変数 $a の値は 4。
| 例 | Mode | 結果 |
|---|---|---|
2 |
表現 | 2 (整数) |
`2 |
表現 | "2" (コマンド) |
Write-Output 2 |
表現 | 2 (整数) |
2+2 |
表現 | 4 (整数) |
Write-Output 2+2 |
引数 | "2+2" (文字列) |
Write-Output(2+2) |
表現 | 4 (整数) |
$a |
表現 | 4 (整数) |
Write-Output $a |
表現 | 4 (整数) |
$a+2 |
表現 | 6 (整数) |
Write-Output $a+2 |
引数 | "4+2" (文字列) |
$- |
引数 | "$-" (コマンド) |
Write-Output $- |
引数 | "$-" (文字列) |
a$a |
表現 | "a$a" (コマンド) |
Write-Output a$a |
引数 | "a4" (文字列) |
a'$a' |
表現 | "a$a" (コマンド) |
Write-Output a'$a' |
引数 | "a$a" (文字列) |
a"$a" |
表現 | "a$a" (コマンド) |
Write-Output a"$a" |
引数 | "a4" (文字列) |
a$(2) |
表現 | "a$(2)" (コマンド) |
Write-Output a$(2) |
引数 | "a2" (文字列) |
すべてのトークンは、ブール型の や文字列
| 例 | Mode | 結果 |
|---|---|---|
Write-Output !1 |
引数 | "!1" (文字列) |
Write-Output (!1) |
式 | False (ブール値) |
Write-Output (2) |
式 | 2 (整数) |
Set-Variable AB A,B |
引数 | 'A','B' (配列) |
CMD /CECHO A,B |
引数 | 'A,B' (文字列) |
CMD /CECHO $AB |
式 | 'A B' (配列) |
CMD /CECHO :$AB |
引数 | ':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 コマンドまたは式として解釈するのを停止できます。
手記
停止解析トークンは、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> が定義された環境変数を参照していない場合、トークンは as-isを介して渡されます。
ストリーム リダイレクト (>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"
手記
PowerShell コマンドレットを使用する場合、停止解析トークンは必要ありません。 ただし、これらの引数を使用してネイティブ コマンドを呼び出すために設計された PowerShell 関数に引数を渡すと便利な場合があります。
引用符文字を含む引数を渡す
Warning
Windowsでは、バッチ ファイルに引数を渡すとき、引数は生のコマンド ライン文字列としてcmd.exeに渡されます。 PowerShell と基になる API はパラメーターを安全に解釈しようとしますが、信頼されていない入力は別の方法で渡す必要があります。
一部のネイティブ コマンドでは、引用符文字を含む引数が必要です。 PowerShell 7.3 では、ネイティブ コマンドのコマンド ラインの解析方法が変更されました。
注意
新しい動作は、Windows PowerShell 5.1 の動作からの 破壊的変更 です。 これにより、ネイティブ アプリケーションを呼び出すときにさまざまな問題を回避するスクリプトと自動化が中断される可能性があります。 必要に応じネイティブ引数の受け渡しを回避するには、停止解析トークン (--%) または Start-Process コマンドレットを使用します。
この動作は、新しい $PSNativeCommandArgumentPassing ユーザー設定変数によって制御されます。 この変数を使用すると、実行時の動作を選択できます。 有効な値は、Legacy、Standard、および Windowsです。 既定の動作はプラットフォーム固有です。 Windows プラットフォームでは、既定の設定は Windows で、Windows 以外のプラットフォームでは既定で Standardに設定されます。
Legacy は過去の動作です。
Windows モードと Standard モードの動作は同じですが、Windows モードでは、次のファイルの呼び出しで Legacy スタイル引数の受け渡しが自動的に使用されます。
cmd.execscript.exewscript.exe-
.batで終わる -
.cmdで終わる -
.jsで終わる -
.vbsで終わる -
.wsfで終わる
$PSNativeCommandArgumentPassing が Legacy または Standardのいずれかに設定されている場合、パーサーはこれらのファイルをチェックしません。
手記
次の例では、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\\""
手記
バックスラッシュ (\) 文字は、PowerShell ではエスケープ文字として認識されません。 これは、ProcessStartInfo.ArgumentListの基になる API によって使用されるエスケープ文字です。
PowerShell 7.3 では、ネイティブ コマンドのパラメーター バインドをトレースする機能も追加されました。 詳細については、「Trace-Command 」を参照してください。
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 コマンドで使用すると、PowerShell はチルダ文字をユーザーのホーム ディレクトリに展開します。 パス内の他の場所でチルダ文字を使用する場合は、リテラル文字として扱われます。
PS D:\temp> $PWD
Path
----
D:\temp
PS D:\temp> Set-Location ~
PS C:\Users\user2> $PWD
Path
----
C:\Users\user2
この例では、 の New-Item パラメーターに文字列が必要です。 チルダ文字はリテラル文字として扱われます。 新しく作成されたディレクトリに変更するには、チルダ文字でパスを修飾する必要があります。
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 7.5-preview.2 では、ネイティブ コマンド用のユーザーのホーム ディレクトリにチルダを展開する試験的な機能が追加されました。 詳細については、「PowerShellで試験的機能を使用する
展開された文字列はネイティブ コマンドに渡されます。 チルダを展開すると、Windows でチルダ文字をサポートしていないネイティブ コマンドのエラーが回避されます。 結果の文字列は、Trace-Commandを使用してパラメーター バインドをトレースすることで確認できます。
Trace-Command -Name ParameterBinding -Expression {
findstr /C:\foo" ~\repocache.clixml
} -PSHost
DEBUG: 2024-05-06 15:13:46.8268 ParameterBinding Information: 0 : BIND NAMED native application line args [C:\Windows\system32\findstr.exe]
DEBUG: 2024-05-06 15:13:46.8270 ParameterBinding Information: 0 : BIND cmd line arg [/C:\oo] to position [0]
DEBUG: 2024-05-06 15:13:46.8271 ParameterBinding Information: 0 : BIND cmd line arg [C:\Users\user2\repocache.clixml] to position [1]
DEBUG: 2024-05-06 15:13:46.8322 ParameterBinding Information: 0 : CALLING BeginProcessing
~\repocache.clixml が C:\Users\user2\repocache.clixmlに展開されていることに注意してください。
こちらも参照ください
PowerShell