So-net無料ブログ作成
検索選択
QLOOKアクセス解析

式モードとコマンドモード [PowerShell]

式モード(expression mode)とコマンドモード(command mode)の違いに注意せず失敗したので、まとめておきます。

まず基本の確認から。

PowerShellでの構文解析には式モードとコマンドモードの二種類があります。おおざっぱに言うと、

式モード: (PowerShell での通常の解釈を行うモード)
    数字は数字、文字列には引用符が必要、など
    
コマンドモード:(コマンドにそのままパラメターとして引き渡すためのモード)
    数字は数字、引用符なしでも文字列は文字列。
    ただし $ と @ と ' と " ( で始める場合は別。
    

行頭からいきなり書き始めると式モードで解析されるので、
PS> abc
用語 'abc' は、コマンドレット、関数、操作可能なプログラム、またはスクリプト ファイルとして認識されません。用語を確認し
、再試行してください。
発生場所 行:1 文字:3
+ abc <<<<
のように引用符のない文字列は単なる文字列ではなく、何か呼び出し可能なものの名前として探されます。
PS> 'abc'
abc
引用符をつければ文字列と扱われます。
数字の場合には、
PS> 11+22
33
のように式モードで数値として評価されます。

代入の右辺でもやはり式モードであり、おなじく、
PS> $str = abc
用語 'abc' は、コマンドレット、関数、操作可能なプログラム、またはスクリプト ファイルとして認識されません。用語を確認し
、再試行してください。
発生場所 行:1 文字:10
+ $str = abc <<<<
PS> $str = 'abc'
PS> $str
abc
のようになります。

これがコマンドレットのパラメターとして扱われる場所ではコマンドモードとなります。
PS> $y = Write-Output abc ; $y
abc
PS> $y = Write-Output 11+22 ; $y
11+22
引用符をつけなくても文字列と解釈され、数字も数値として計算されません。

さてここで私が起こしたミスの話です。

New-Object コマンドレットで .NET Frameworkオブジェクトを作成するときに、そのコンストラクターに引数を渡す場面です。
望ましい書き方は次のようになります。
$m = New-Object System.Threading.Mutex ($false, "MyMutex")
ミューテックスのコンストラクターに論理値の偽と名前の文字列を渡しています。
二つのパラメターは () で囲まれているので式モードで解釈され、要素数2の配列が渡されることになります。
そのようすはたとえば次のように確認できます。
PS> $x = Write-Output ($false, "MyMutex")
PS> $x
False
MyMutex
PS> $x.GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array


PS> $x[0].gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Boolean System.ValueType


PS> $x[1].gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
ところが、私はこれを最初 () なしで書いていました。
さらに次のように $false の $ を書き洩らしていました。
$m = New-Object System.Threading.Mutex false, "MyMutex"
するとどうなるでしょう。
まず $ を書き洩らしていない場合を考えます。
$m = New-Object System.Threading.Mutex $false, "MyMutex"
実はこの場合には問題なく動作するのです。
先ほどと同じように確認すると次のようになります。
PS> $x = Write-Output $false, "MyMutex"
PS> $x
False
MyMutex
PS> $x.GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array


PS> $x[0].gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Boolean System.ValueType


PS> $x[1].gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
おなじように論理値と文字列の配列が渡されているのがわかります。
今度は$を抜かしてしまった場合の動作です。
PS> $x = Write-Output false, "MyMutex"
PS> $x
false
MyMutex
PS> $x.GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array


PS> $x[0].gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object


PS> $x[1].gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object

今度は最初の要素は論理値ではなく文字列になっています。
これがSystem.Threading.Mutexのコンストラクターに渡された時に、型の違いでエラーが出たりするわけではなく、最初のパラメターが論理値の真と解釈されて動作してしまいます。
書いたつもりと反対になってしまうわけですね。

このような場所では () で囲ってPowerShellの式モードでの構文チェックを通して、エラーを見つけて貰った方が安心であるという教訓を得たのでした。

囲っておけば次のようにエラーになります。
PS> $x = Write-Output (false, "MyMutex")
用語 'false' は、コマンドレット、関数、操作可能なプログラム、またはスクリプト ファイルとして認
識されません。用語を確認
し、再試行してください。
発生場所 行:1 文字:25
+ $x = Write-Output (false, <<<< "MyMutex")
PS>


どこがコマンドモードとして扱われるのか、にも注意が必要ですね。
それをわかりやすくまとめたところはどこかに無いかなあ。。。
タグ:powershell
メッセージを送る
人気ブログランキングへ
 

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。

×

この広告は1年以上新しい記事の更新がないブログに表示されております。