自動変数

PowerShell Coreには実行中にPowerShell Coreによって生成される変数がある。この変数は「自動変数」と呼ばれており、リードオンリーでの利用が想定されている。機能として自動変数を上書きすることはできるのだが、互換性の面からそういった利用はしない方がよいとされている。

PowerShell Coreに用意されている主な自動変数は次のとおり。

自動変数 意味
$NULL NULLまたは空値を保持
$TRUE TRUEを保持
$FALSE FALSEを保持
$? 最後に実行した操作の可否を保有。成功した場合にはTRUE、失敗した場合にはFALSE
$$ セッションが最後に受け取った行の最後のトークン
$^ セッションが最後に受け取った行の最初のトークン
$ARGS 関数、スクリプト、スクリプトブロックなどに渡されたパラーメタの配列
$CONSOLEFILENAME セッションで最後に実行されたコンソールファイル(.psc1)のパス
$ERROR エラーメッセージが保持された配列。もっとも最後のエラーは$ERROR[0]に保持される
$EVENT 処理されているイベントのPSEventArgsオブジェクトを保持
$EVENTARGS 処理されているイベントのEventArgsから派生した最初のイベント引数を示すオブジェクトを保持
$EVENTSUBSCRIBER 処理されているイベントのイベントサブスクライバを示すPSEventSubscriberオブジェクトを保持
$EXECUTIONCONTEXT PowerShellホストの実行コンテキストを意味するEngineIntrinsicsオブジェクトを保持
$FOREACH ForEachループの列挙子を保持
$HOME ユーザのホームディレクトリパスを保持
$HOST PowerShellの現在のホストアプリケーションを示すオブジェクトを保持
$INPUT 関数に渡されるすべてのインプットを示す列挙子を保持
$LASTEXITCODE 最後に実行されたWindowsベースプログラムの終了コードを保持
$MATCHES -matchオペレータおよび-notmatchオペレータとともに動作する$Matches変数
$MYINVOCATION 現在のコマンド(名前、パラメータ、パラメータ変数、コマンドが実行された方法など)に関する情報を保持
$NESTEDPROMPTLEVEL 現在のプロンプトレベルを保持
$PID プロセス番号を保持
$PROFILE 現在のユーザおよび現在のホストアプリケーションに対するPowerShellプロファイルのフルパスを保持
$PSBOUNDPARAMETERS スクリプトや関数に渡されるパラーメタのディクショナリおよび現在の値を保持
$PSCMDLET 実行されているコマンドレットまたはアドバンスド関数を示すオブジェクトを保持
$PSCOMMANDPATH 実行されているスクリプトのファイル名およびフルパスを保持
$PSCULTURE 現在実行されているオペレーティングシステムの文化名を保持
$PSDEBUGCONTEXT デバッグ中にデバッグ情報を保持
$PSHOME PowerShellがインストールされているディレクトリのフルパスを保持
$PSITEM パイプラインオブジェクトにおける現在のオブジェクトを保持。$_に同じ
$PSSCRIPTROOT スクリプトが実行されているディレクトリを保持
$PSSENDERINFO PSSessionを開始したユーザに関する情報を保持。ユーザ情報やタイムゾーンなどの情報を含む
$PSUICULTURE オペレーティングシステムで使われているユーザインターフェース文化名を含む
$PSVERSIONTABLE 現在のセッションで実行されているPowerShellのバージョン情報を示すハッシュテーブルを保持
$PWD カレントディレクトリのフルパスを示すオブジェクトを保持
$SENDER 対象イベントによって生成されたオブジェクトを保持
$SHELLID 現在のシェル情報を保持
$STACKTRACE エラーを示すスタックトレースを保持
$THIS スクリプトプロパティやスクリプトメソッドを定義するスクリプトブロックにおいて拡張されているオブジェクトを参照

すべての自動変数を覚える必要はなく、必要なものだけ覚えておけばよい。以降でよく使うことになる自動変数とその使用例を取り上げる。

$TRUEと$FALSE

スクリプトを書いていると、ロジック的にここに処理がきた場合にはエラーとして処理したいとか、逆にコマンドの実行結果が成功していても失敗していても成功として処理させたいことがある。UNIX系オペレーティングシステムのシェルスクリプトであればtrueとfalseというコマンドが存在しているので、これを使うことになる。

trueアプリケーション - PowerShell Core/macOS

PS /Users/daichi> Get-Command true | Format-List


Name            : true
CommandType     : Application
Definition      : /usr/bin/true
Extension       :
Path            : /usr/bin/true
FileVersionInfo : File:             /usr/bin/true
                  InternalName:
                  OriginalFilename:
                  FileVersion:
                  FileDescription:
                  Product:
                  ProductVersion:
                  Debug:            False
                  Patched:          False
                  PreRelease:       False
                  PrivateBuild:     False
                  SpecialBuild:     False
                  Language:




PS /Users/daichi>

falseアプリケーション - PowerShell Core/macOS

PS /Users/daichi> Get-Command false | Format-List

Name            : false
CommandType     : Application
Definition      : /usr/bin/false
Extension       :
Path            : /usr/bin/false
FileVersionInfo : File:             /usr/bin/false
                  InternalName:
                  OriginalFilename:
                  FileVersion:
                  FileDescription:
                  Product:
                  ProductVersion:
                  Debug:            False
                  Patched:          False
                  PreRelease:       False
                  PrivateBuild:     False
                  SpecialBuild:     False
                  Language:




PS /Users/daichi>

しかし、このコマンドはWindowsには存在しておらず、実行しようとすれば次のようにエラーになる。

trueとfalse - PowerShell / Windows

自動変数の$TRUEおよび$FALSEはそういった場合に利用できる。この変数を利用すればtrueやfalseといったコマンドが存在しないプラットフォームでも処理が機能することになる。

$?

$?は直前に実行したコマンドが成功しているか失敗しているかを保持する自動変数だ。常に成功するtrueと常に失敗するfalseというアプリケーション(UNIX系オペレーティングシステムのコマンド)を使って動作を確認すると次のようになる。

直前のコマンドが成功した場合の$?の中身

PS /Users/daichi> true
PS /Users/daichi> echo $?
True
PS /Users/daichi>

直前のコマンドが失敗した場合の$?の中身

PS /Users/daichi> false
PS /Users/daichi> echo $?
False
PS /Users/daichi>

この自動変数は直前のコマンドの実行を監視して、失敗した場合にはエラーメッセージを出すようにするとか、何らかの処理をリトライするとか、そういった動作をさせる場合に利用できる。

これを簡単に書くと次のようになる。

直前のコマンドが成功している場合には何もしない

PS /Users/daichi> true
PS /Users/daichi> if (! $?) { echo "failed." }
PS /Users/daichi>

直前のコマンドが失敗している場合にはエラーメッセージを出力

PS /Users/daichi> false
PS /Users/daichi> if (! $?) { echo "failed." }
failed.
PS /Users/daichi>

例えば、ネットワークを経由してほかのホストにファイルをコピーするような処理を書いたとする。それはscpでもftpでもなんでもよいのだが、ネットワークを経由してほかのホストにコピーするような処理では、必ずしも処理が成功するとは限らない。

そういった場合、コマンドが成功裏に終了したかどうかを調べ、失敗している場合にはある程度の時間待機してからもう一度同じコマンドを実行するといった処理を行うことがある。$?を使うとそうした処理を実現できる。

$ARGS

PowerShellスクリプトを書くようになると関数を作成する機会が増えると思うが、こういった場合に引数が格納されるのが$ARGSだ。

引数の利用例

PS /Users/daichi> function f() { echo $ARGS }
PS /Users/daichi> f a b c d
a
b
c
d
PS /Users/daichi>

基本的な機能なので覚えておく必要がある。

$HOMEと$PWD

$HOMEにはホームディレクトリ、$PWDにはカレントディレクトリが保持される。どちらも使う機会のある自動変数だ。

$HOMEはユーザのホームディレクトリ以下のデータにアクセスする場合に利用するし、$PWDはカレントディレクトリによって処理を変えたい場合などに利用する。

$ERRORと$STACKTRASE

インタラクティブシェルとして利用している場合も、シェルスクリプトとして利用している場合も、問題が発生した場合のデバッグに利用できる変数が$ERRORと$STACKTRASEだ。

$Errorが保持しているエラー情報

PS /Users/daichi> echo $Error
At line:1 char:3
+ if $(! $?)
+   ~
Missing '(' after 'if' in if statement.
s : The term 's' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:2
+ ;s
+  ~
+ CategoryInfo          : ObjectNotFound: (s:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

PS /Users/daichi>

$StackTraceが保持しているスタックトレース情報

PS /Users/daichi> echo $StackTrace
   at System.Management.Automation.CommandDiscovery.LookupCommandInfo(String commandName, CommandTypes commandTypes, SearchResolutionOptions searchResolutionOptions, CommandOrigin commandOrigin, ExecutionContext context)
   at System.Management.Automation.CommandDiscovery.LookupCommandInfo(String commandName, CommandOrigin commandOrigin, ExecutionContext context)
   at System.Management.Automation.CommandDiscovery.LookupCommandInfo(String commandName, CommandOrigin commandOrigin)
   at System.Management.Automation.CommandDiscovery.LookupCommandProcessor(String commandName, CommandOrigin commandOrigin, Nullable`1 useLocalScope)
   at System.Management.Automation.ExecutionContext.CreateCommand(String command, Boolean dotSource)
   at System.Management.Automation.PipelineOps.AddCommand(PipelineProcessor pipe, CommandParameterInternal[] commandElements, CommandBaseAst commandBaseAst, CommandRedirection[] redirections, ExecutionContext context)
   at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
   at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
PS /Users/daichi>

すべての自動変数を覚える必要はないが、今回取り上げたような自動変数は利用する機会が多いのでおさえておきたいところだ。

参考資料