PowerShellの基本的なことがわかってなかったシリーズ第7弾です。
基本的にはヘルプを見ればわかる話ですが、ヘルプではわかりにくかった部分があったので補足する形でこのエントリを書きます。
Set-StrictModeについて
Get-Help Set-StrictMode -Full
でこのコマンドレットのヘルプを見ると、
式、スクリプト、およびスクリプト ブロックのコーディング規則を設定して適用します。
と書いてあります。
なんだか微妙な説明ですが、要はPowerShellスクリプト構文のエラーチェックをより厳格にする機能になります。
Set-StrictMode -Version
で指定したバージョンに応じた規則を追加し、Set-StrictMode -Off
で規則を解除できます。
規則が適用されるのはSet-StrictMode
を実行したスコープになります。
類似の機能にVBScriptのOption Explicit
なんかがあったりしますがSet-StrictMode
はどちらかというとデバッグ用の側面が強いみたいです。
Set-StrictMode -Versionのバージョン指定について
-Version
パラメーターで指定できるのは1.0
、2.0
、Latest
なのですが、引数のチェック自体はPowerShellのバージョンと対応する様になっている様で3.0~5.0
の値も環境に応じて指定することができます。
ただ、(追記あり)2.0
以上のバージョンで規則は増えていないため2.0=3.0~5.0
です。
また、Latest
選んだ場合は実行しているPowerShellのバージョンと同じ値が設定される様です。
ここで一つだけ注意しておきたいのは、-Version
パラメーターに指定できる値はPowerShellのバージョンと関連のある値ですが、設定するのはあくまでも規則の厳格さに過ぎず、追加される規則はPowerShellのバージョンと連動するものでは無いということです。
例えばpowershell.exeの起動時引数の-Version
パラメーターみたいな効果は無いということです。
正直このパラメーター名は-Version
より-Level
とかの方が適切だと思います。
-- 2015/11/21追記 --
2.0
以上のバージョンで規則は増えていないため2.0=3.0~5.0
です。
について、公式には上記が謳われているのですがドキュメント化されていない規則が3.0
にある様です。
Set-StrictMode -Version 1.0で追加される規則
ここから-Version
毎に追加される規則ついて触れていきます。
ヘルプを見れば十分な部分もありますが、本エントリではちょっとした補足もありますので順に説明していきます。
Set-StrictMode -Version 1.0
を実行すると以下の規則が追加されます。
1. 初期化されていない変数 (文字列の初期化されていない変数を除く) への参照を禁止します。
これはわかりやすいと思います。
$a = 123
の様に変数定義時に初期化しないとエラーになる規則が追加されます。
Set-StrictMode -Off
だと問題ないコード
PS C:\> Set-StrictMode -Off PS C:\> $a PS C:\>
が、Set-StrictMode -Version 1.0
にすると以下の様にエラーになります。
PS C:\> Set-StrictMode -Version 1.0 PS C:\> $a 変数 '$a' は、設定されていないために取得できません。 発生場所 行:1 文字:1 + $a + ~~ + CategoryInfo : InvalidOperation: (a:String) []、RuntimeException + FullyQualifiedErrorId : VariableIsUndefined
ここで "文字列の初期化されていない変数を除く" というのは、文字列中の変数定義("$a"
みたいな使い方)に対しては初期化されていない変数を使ってもエラーとはならないという事を指しています。
# 変数$bは未定義 # 文字列中の変数定義は初期化されていなくてもエラーにはならない PS C:\> Set-StrictMode -Version 1.0 PS C:\> "$b" PS C:\>
Set-StrictMode -Version 2.0で追加される規則
続けてSet-StrictMode -Version 2.0
を実行すると以下の規則が追加されます。
1. 初期化されていない変数 (文字列の初期化されていない変数を含む) への参照を禁止します。
これは-Version 1.0
の規則と同等ですが、 "文字列の初期化されていない変数を含む" とある様に、文字列中の変数定義でも初期化されていない変数が使われるとエラーとなります。
# 変数$cは未定義 # 文字列中の変数定義も初期化されていない場合はエラーとなる PS C:\> Set-StrictMode -Version 2.0 PS C:\> "$c" 変数 '$c' は、設定されていないために取得できません。 発生場所 行:1 文字:2 + "$c" + ~~ + CategoryInfo : InvalidOperation: (c:String) []、RuntimeException + FullyQualifiedErrorId : VariableIsUndefined
2. オブジェクトに存在しないプロパティへの参照を禁止します。
これもわかりやすいと思います。
Set-StrictMode -Off
だと存在しないプロパティへのアクセスは$null
を返して終わりますが、Set-StrictMode -Version 2.0
ではエラーとなります。
PS C:\> Set-StrictMode -Version 2.0 PS C:\> (123).ErrorProperty このオブジェクトにプロパティ 'ErrorProperty' が見つかりません。プロパティが存在することを確認してください。 発生場所 行:1 文字:1 + (123).ErrorProperty + ~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], PropertyNotFoundException + FullyQualifiedErrorId : PropertyNotFoundStrict
また、
のエントリで記載した非配列オブジェクトに対するCount
プロパティもこの場合はエラーとなります。
PS C:\> Set-StrictMode -Version 2.0 PS C:\> (123).Count このオブジェクトにプロパティ 'Count' が見つかりません。プロパティが存在することを確認してください。 発生場所 行:1 文字:1 + (123).Count + ~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], PropertyNotFoundException + FullyQualifiedErrorId : PropertyNotFoundStrict
3. メソッドを呼び出すための構文を使用した関数の呼び出しを禁止します。
これはちょっとわかりにくいのですが、ヘルプにあるサンプルを見ると理解しやすいと思います。
function add ($a, $b) {$a + $b}
というシンプルなファンクションがあった場合、通常はadd 3 4
の様に引数をスペース区切りで使用しますが、間違ってadd(3,4)
と記述してもエラーとならず意図しない動作になってしまいます。
# 意図した動作 PS C:\> add 3 4 7 # 意図しない動作 # add (3,4) $null => (3,4) + $null と解釈されてしまう。 PS C:\> add(3,4) 3 4
Set-StrictMode -Version 2.0
にすると上記の様な()
を使ったファンクション呼び出しがエラーとなります。
PS C:\> Set-StrictMode -Version 2.0 PS C:\> add(3,4) 関数またはコマンドが、メソッドのように呼び出されました。パラメーターは、スペースで区切られる必要があります。パラメータ ーの詳細については、about_Parameters ヘルプ トピックを参照してください。 発生場所 行:1 文字:1 + add(3,4) + ~~~~~~~~ + CategoryInfo : InvalidOperation: (:) []、RuntimeException + FullyQualifiedErrorId : StrictModeFunctionCallWithParens
ただ、残念なことにメソッド名の直後に(
があるかどうかだけで判断している様で以下の様な場合はエラーになりません。
# addの直後にスペースがあるだけでこの制約は動作しない # まあ当然といえば当然ですが... PS C:\> Set-StrictMode -Version 2.0 PS C:\> add (3,4) 3 4
個人的にはこの様な動作を防止するにはfunction add ([int]$a, [int]$b) {$a + $b}
の様に引数にきちんと型指定をする方が良いと思います。
4. 名前のない変数 (${}) を禁止します。
これはヘルプの日本語が足りなくて*1暫く理解できなかったのですが、文字列中の変数定義の話になります。
PowerShellでは${a}
の様に{}
でくくる形式でも変数名を定義できます。
この形式は主に文字列内での変数定義に使われると思います。
# 変数定義を明確にするために{}でくくる PS C:\> $a=123 PS C:\> "${a}45" 12345
PowerShell 2.0までは文字列中の変数定義に"${}"
と書いてもエラーになりませんでした。
# PowerShell 2.0 PS C:\> "${}" $
Set-StrictMode -Version 2.0
にするとこれがエラーとなります。
# PowerShell 2.0 PS C:\> Set-StrictMode -Version 2.0 PS C:\> "${}" 中かっこで囲まれた変数名は、空にできません。 発生場所 行:1 文字:6 + "${}" <<<< + CategoryInfo : InvalidOperation: (:) []、RuntimeException + FullyQualifiedErrorId : EmptyBracedVariableName
ただ、この仕様はPowerShell 3.0からSet-StrictMode -Off
でもエラーとなる様に変更されています。
# PowerShell 3.0以降 PS C:\> Set-StrictMode -Off PS C:\> "${}" 発生場所 行:1 文字:4 + "${}" + ~ 空の ${} 変数参照が見つかりました。中かっこ内に名前が必要です。 + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : EmptyVariableReference
この規則は今となってはあまり意味を成さない感じになっています。
2015/11/21追記 Set-StrictMode -Version 3.0で追加される規則
公式にドキュメント化はされていないのですが、Set-StrictMode -Version 3.0
で以下の規則が追加されている様です。
今のところ1つだけわかりましたが他にもあるかもしれません。
【2018/04/18追記】
現時点でPowerShell Coreのソースを検索した限りではSet-StrictMode -Version 3.0
の規則はこの1つだけでした。
あと、ちょっと前のはなしですがドキュメントが無い旨をIssueに上げておきました。
【追記ここまで】
1. 配列の境界チェックの追加
http://blogs.technet.com/b/heyscriptingguy/archive/2015/07/15/use-powershell-strict-mode-for-debugging.aspxblogs.technet.com
のコメント欄にこの規則に対する指摘がされています。
-Version 2.0
までは配列の境界外のアクセスは$null
を返すだけですが-Version 3.0
にすると配列の境界外へのアクセスがエラーになります。
# -Version 2.0 までは配列の境界外へのアクセスは $null を返すだけ PS C:\> Set-StrictMode -Version 2.0 PS C:\> @(1,2,3)[2] 3 PS C:\> @(1,2,3)[3] # -Version 3.0 からは配列の境界外へのアクセスはエラーとなる PS C:\> Set-StrictMode -Version 3.0 PS C:\> @(1,2,3)[2] 3 PS C:\> @(1,2,3)[3] インデックスが配列の境界外です。 発生場所 行:1 文字:3 + @(1,2,3)[3] + ~~~~~ + CategoryInfo : OperationStopped: (:) [], IndexOutOfRangeException + FullyQualifiedErrorId : System.IndexOutOfRangeException # PowerShellでは負数のインデックス指定ができるがこれもエラーとなる。 PS C:\> @(1,2,3)[-3] 1 PS C:\> @(1,2,3)[-4] インデックスが配列の境界外です。 発生場所 行:1 文字:3 + @(1,2,3)[-4] + ~~~~~ + CategoryInfo : OperationStopped: (:) [], IndexOutOfRangeException + FullyQualifiedErrorId : System.IndexOutOfRangeException
日本語で出せとまでは言いませんが規則が増えたのなら公式にドキュメント化してほしいものです...
Get-StrictModeについて
Set-StrictMode
があるならGet-StrictMode
もありそうなのですが、標準には存在せず、ScriptCenterに有志が作ったものがある程度となっています。
スクリプトの中身を見るとリフレクションを使って現在のスコープオブジェクトの非パブリックなプロパティからバージョンを取得しておりなかなか楽しい感じです。
実行例は以下の様な感じになります。
PS C:\> Set-StrictMode -Version 1.0 PS C:\> Get-StrictMode Major Minor Build Revision ----- ----- ----- -------- 1 0 -1 -1 PS C:\> Set-StrictMode -Version 2.0 PS C:\> Get-StrictMode Major Minor Build Revision ----- ----- ----- -------- 2 0 -1 -1 # PowerShell 4.0 の場合 PS C:\> Set-StrictMode -Version Latest PS C:\> Get-StrictMode Major Minor Build Revision ----- ----- ----- -------- 4 0 -1 -1 PS C:\> Set-StrictMode -Off PS C:\> Get-StrictMode Major Minor Build Revision ----- ----- ----- -------- 0 0 -1 -1
*1:英語の原文も足りてない