PowerShellを知ろう

PowerShellのプログラミング言語としての文法は比較的簡単ではないかと考えている。しかし、操作する対象のオブジェクトが何なのかによって動きを変えてしまうところが気が利いているようで混乱する元となっている。

ここではPowerShellの基本的なプログラミング言語としての側面をお話ししたい。rubyやpythonのような一般的なプログラミング言語、C#のような.Net Framework向けプログラミング言語とも異なる、それでいて関数型でもオブジェクト指向でもない不思議なPowerShellについて見ていこう。

変数について

PowerShellにおける変数はシェルやコマンドプロンプトにおける環境変数のようでもあり、スクリプト言語の変数のようでもあり、.Net Frameworkオブジェクトでもあるという不思議な存在である。このため、無用なトラブルを簡単に発生させることもできる。よくプログラミング言語において変数のことを説明しようとすると、箱が登場することが多いだろう。PowerShellにおいて変数は、箱というよりは得体の知れない何かと表現したほうがいいかもしれない。

PowerShellにおいて変数を表すには先頭にドル記号をつける。このあたりはperlやPHPのようでもあるし、bashなどとも似ているところがあるのだが、bashとの違いは代入のときにもドル記号が必要で、イコール記号と変数名や値の間に半角スペースが入ってもよいということだろう。

$aaa = 1

これは変数の名前が「aaa」であり、その値が数値の「1」であるということがわかる。PowerShellでは代入された時点で初めて変数として扱われるようになる。そのため、存在しない変数を使用しても何も起こらない。いわゆるnull(ヌル)という状態だ。大抵のプログラミング言語ではnullを参照しようとするとアクセス違反や未定義の変数だというエラーが発生するだろう。PowerShellではユーザーが明示的に意思表示しないかぎりエラーにはならない。すべてスルーしてしまう。これはダメだ!!と思うか、サクサク書いていってちょっとしたツールを今日中に仕上げる!と考えるかはユーザー次第というところだが、ダメだと思うユーザー向けにはコマンドレットが用意されている。「Set-StrictMode」コマンドレットだ。どのPowerShellバージョンをターゲットにするかを教えてあげることで、未定義の変数へアクセスしようとするとエラーになるようにしてくれるのだ。

Set-StrictMode -Version 5

こうしたところの判断をユーザーに委ねているというところは議論があるのかもしれない。しかし、いつでも厳密にされるよりは効率をアップすることを優先したい場合にはよりよいかもしれない。

PowerShellの変数

PowerShellにおいて、変数とは箱ではないというのは前述したとおりなのだが、なぜ箱ではないのかについてお話ししておきたい。PowerShellでは変数というくくり自体がファイルシステムやWindowsレジストリなどといったものと同じように扱われている。そのため、不思議なことができる。変数を定義するために、コマンドレットを利用するということが可能になっている。

Set-Variable -Name aaa -Value 1

この命令はさきほどご紹介した変数へ数値1を代入したものと同じ意味になるものだ。コマンドレットを使って変数を定義することができる。変数にアクセスするには変数名を使うだけなのだが、これまたコマンドレットを利用することができる。

Get-Variable -Name aaa  #==> 1

これは変数名でアクセスしたことと同じ結果になる。

さて、コマンドレットで変数を宣言することができると述べたのだが、実際のところは宣言しているわけではない。それはコマンドレットの名前からも見て取れる。何かにセットしているのだ。名前が「aaa」で値が数値の1だ。何にセットしているのか?それは変数を管理しているものである。

ためしに次のようにコンソールで入力して実行してみてほしい。

Variable

すると、ずらりと出力されたはずだ。そして、その中にNameが「aaa」になっているものを見つけることができるだろう。変数を宣言するコマンドレットは、ここへセットしているのだ。これはVariableドライブと呼ばれる。変数名に$をつけてアクセスすると、このVariableドライブの中から名前が変数名に合致しているオブジェクトを取り出し、その値を返しているということになる。なんだか気持ち悪いと感じるのは他のプログラミング言語に慣れているせいかもしれない。

さて、ここまででPowerShellの変数は変数の集まりに含まれるオブジェクトだということがお分かりになっただろう。こうなると、値をクリアしたり変数のエントリーを削除したりしたくなるものだ。値をクリアするには専用のコマンドレットが用意されている。

Clear-Variable -Name aaa

これで変数名「aaa」の値はクリアされる。しかし変数のエントリー自体が消えるわけでない。さきほどのようにVariableを実行すると名前を見つけることができるはずだ。これはまだ変数としてのエントリーが残っているためであり、ここから変数を完全に削除してやるには、これまた専用のコマンドレットが用意されている。

Remove-Variable -Name aaa

これを実行すると、Variableを実行しても名前を見つけることはできなくなる。使われなくなった変数という扱いになる。

変数のデータ型

PowerShellの変数はさまざまなデータ型を利用することができる。基本的に.Net Frameworkのオブジェクトであるため、C#で利用できるものはほぼ利用できると考えていいだろう。また、.Net Frameworkで利用できるデータ型の別名が用意されているデータ型もある。データ型は角かっこを使って[string]といったように利用する。以下によく利用する主なデータ型を挙げた。

PowerShellデータ型 データ型 概要
[int] System.Int32 32ビット整数
[string] System.String 文字列
[char] System.Char 1文字
[datetime] System.DateTime 日付、時刻
[double] System.Double 倍精度浮動小数点

データ型がいつ決定されるのかについては、変数に与えられた値がどんなデータなのかによって決定される。だから、数値の1を与えたとしたらその変数のデータ型は数値型に決定されるということだ。このあたりはスクリプト言語のrubyやpythonなどとも共通している。これは便利!ということもあるが、一方では厳密に「ここはテキストなんだ」というケースもありうるだろう。そうした場合のため、PowerShellでは変数名の前にデータ型を指定することができるようになっている。

[string]$aaa = "abc"

この変数「aaa」はテキストになっている。このため、数値の1や真偽を表す$falseといったものを代入しても、すべてテキストに自動的に変換されてから値としてセットされる。

PowerShellにおいて、変数のデータ型を確認するためには、変数オブジェクトが持っている「GetType」メソッドを利用する。これによって変数オブジェクトが現在どのデータ型なのかを知ることができる。

$aaa.GetType()

ここではデータ型のNameはStringとして出力されるはずだ。つまり、テキストだ。

寿命や見える範囲

ほかのプログラミング言語と同様に変数が存在していられる範囲、スコープが存在している。この寿命ともいうべきスコープはある程度はユーザーが指定することができる。また、指定しない場合はローカルスコープが指定される。

PowerShellの変数におけるスコープの範囲としては大きく3つある。

  • グローバル
  • スクリプト
  • ローカル

まずグローバルというのはそのセッション中のどの状況からでもアクセスすることができるようになっているものだ。明示的に削除しない限りはどこからでもアクセスできるし、変更することもできる。次のスクリプトというのは実行中のスクリプトファイルの中からはどこからでもアクセスできるというものだ。スクリプトが終了すると寿命を終える。ほかのプログラミング言語でいうところのいわゆるグローバルのことだろう。PowerShellにおいてこれより上のスコープであるグローバルが別に存在するのは、対話型のシェルとしての利用があるためだと考えられる。

一方、ローカルについては変数がエントリーされた場所、タイミングによりさまざまな寿命を持つことになる。このあたりについてはスクリプトブロックについての話で再び登場する。

次は可視性とも呼ばれる、変数がどの範囲で見えるのかということについてだ。PowerShellではC#などとは異なり、PublicかPrivateの2種類しか存在しない。この違いは変数をエントリーしたスクリプトの外のスクリプトからも見えるか、見えないかだけを決定している。そして何も指定しない場合はPublicになる。この考え方が必要になるのは複数のスクリプトファイルを利用して処理を行うような場合に、別のスクリプトから変更されたくないなどの要求があるとき利用すればよい。

まとめると、基本的には見える範囲はPublicであり、あとはそのスコープをどこまでにするかということだ。ローカルの場合は変数が最初に使用されたスクリプトブロック内ということになる。これは暗黙的にそうなる。グローバルやスクリプトスコープにするためにはドル記号のあとにスコープの識別子を付与することになるが、このあたりもPowerShellは自動的に行ってしまう。つまり、意図的につけない場合はPowerShellの判断に委ねられてしまう。

# グローバル変数
$Global:aaa = 1

# スクリプト変数
$Script:bbb = 2

これらのスコープ指定がない場合は、どの段階のスクリプトブロックで使用されたかによって判定される。つまり、スクリプトファイルに直接、変数使用を記載した場合はその変数はグローバル変数とされる。このあたりはJavaScriptなどとも同じであり、変数を限定的に利用したい場合は関数やスクリプトブロックで囲むなどして保護する必要がある。

定数について

PowerShellには定数というものは存在しない。スクリプト言語に多いのだが、変更できないようにする、あるいは変更しようとするとエラーにするといったものがほとんどだろう。PowerShellにおいてもSet-Variableコマンドレットに-Optionパラメータを利用することで読み取り専用の変数にすることや削除ができない変数にすることができる。

近年の関数型プログラミング言語の流行やスクリプト言語で開発されたアプリケーションが巨大化していくにつれ、変数の内容を変更できないようにするということが重要視されている。PowerShellにおいてはコマンドレットを利用することで変数を読み取り専用にすることができる。ただし、ReadOnlyオプションを-Optionパラメータにつけて読み取り専用としたとしても、同様にSet-Variableコマンドレットに-Forceパラメータをつけて実行した場合には値を変更することができる。どうしても変更されたくない場合はConstantオプションを指定すべきだろう。以下は読み取り専用にした変数と変更も削除もできないようにした変数をエントリーするコード例である。

# 読み取り専用変数
Set-Variable -Name aaa -Value 1 -Option ReadOnly

# 上記変数aaaの値を更新するには、次のようにする
Set-Variable -Name aaa -Value 3 -Force


# 削除もできない変数
Set-Variable -Name bbb -Value 2 -Option Constant

# 削除しようとすると、次のようなエラーが発生する
## Remove-Variable : Cannot remove variable bbb because it is ## constant or read-only. If the variable is read-only, try the operation again specifying the Force option.
## At line:1 char:1
## + Remove-Variable -Name bbb
## + ~~~~~~~~~~~~~~~~~~~~~~~~~
##     + CategoryInfo          : WriteError: (bbb:String) [Remove-Variable], SessionStateUnauthorizedAccessException
##     + FullyQualifiedErrorId : VariableNotRemovable,Microsoft.PowerShell.Commands.RemoveVariableCommand

データ型の変換

PowerShellでスクリプトを書いていくようになるとデータ型がどのようになるかが気になるところだろう。たとえば、以下のようなコードを実行すると結果はどうなるだろうか。

1 + "1"

node.jsではテキスト、つまり文字列の「11」となる。rubyとpythonではエラーになる。これは数値オブジェクトと文字列オブジェクトは加算できないからということだ。一方、PowerShellではどうなるだろうか?結果は、数値の2が返される。では逆にしてみてはどうだろうか。

"1" + 1

node.jsではやはり文字列で「11」で変わらず、rubyやpythonでも変わらずエラーのままだ。PowerShellでは?結果は文字列で「11」とnode.jsと同じ結果になる。同じようなコードで一貫した結果にならない!ここで机の上をガサーっと払い落としてしまいたくなること間違いなしだ。おっと、ここでまさかのVBScriptで確認してみるとどちらのコードも結果は数値の2になった。Windows由来のツールだから、という要因ではないようだ。

勘のよい方はお気づきになったかもしれない。この結果をもたらしているのはPowerShellでは左側にくるデータのデータ型に自動的に変換されるからだ。つまり最初のコードでは左側が数値の1であるため、右側の文字列「1」は数値へ変換されてから加算されている。そして次のコードでは左側が文字列のため、右側の数値1が文字列へ変換されてから加算されている。このほかにも自動的に気を利かせてくれることが多いので、ほかのプログラミング言語からPowerShellへ入門する方はご注意願いたい。

ところで、足される数が数でなかった場合はどうなるのだろうか?たとえば次のようなコードの場合だ。

1 + "a"

これはさすがのPowerShellでもエラーになる。理由は簡単で、文字列「a」は自動的に数値型へ変換することができないからだ。よって、逆のコードは「a1」を返す。数値1は文字列に自動的に変換可能だからだ。

もしもこの挙動がまずい、危険だ!と感じた場合はデータ型を明示するべきだ。すると実行時エラーとなるからだ。実行時エラーになる前に確認したければ、前述したとおりGetType()メソッドを利用してデータ型が期待している状態になっているかを確認する必要があるだろう。

results matching ""

    No results matching ""