Windows PowerShell 入門(8)-関数編3

时间:2022-08-01 19:13:14

この連載では、Microsoftが提供している新しいシェル、Windows PowerShellの使い方を解説します。今回は、フィルタ、スクリプトブロック、変数のスコープについて取り上げます。

はじめに

 この連載では、Microsoftが提供している新しいシェル「Windows PowerShell」の使い方を解説します。今回は、フィルタ、スクリプトブロック、変数のスコープについて説明します。

対象読者

  • Windows PowerShellでコマンドレット操作ができる方
  • 何らかのプログラミング経験があればなお良い

必要な環境

  • Windows PowerShell

フィルタ

 PowerShellには、関数とは別にパイプを通して受け取ったオブジェクトを処理するフィルタと呼ばれるものがあります。書式は関数とほぼ一緒なのですが、functionの代わりにfilterキーワードを使用します。

filter <フィルタ名> (引数) {実行するスクリプト}

 関数と同様、結果をreturnで返すことも可能です。

フィルタの利用例

 唐突ですが、ここで問題です。「現在稼働しているサービス一覧を取得してください」と言われたらどうしますか?

 解答例を示します。

PS > Get-Service | Where { $_.Status -eq "Running" }

Status   Name               DisplayName
------ ---- -----------
Running AeLookupSvc Application Experience
Running AppHostSvc Application Host Helper Service
Running AudioEndpointBu... Windows Audio Endpoint Builder
Running Audiosrv Windows Audio
Running BFE Base Filtering Engine
:
: ≪長いので省略≫
:

 Get-ServiceコマンドレットとWhereを使用してステータスが"Running"のものをフィルタしています。この $_.Status -eq "Running" はフィルタ化することが可能です。

 では、フィルタ名を StsRunning としてフィルタを作成してみましょう。

PS > filter StsRunning {
>> if ( $_.Status -eq "Running" )
>> {
>> return $_
>> }
>> }
>>

 こうして作成したフィルタの使用方法は簡単で、パイプの後ろに記述するだけです。

PS > Get-Service | StsRunning

Status   Name               DisplayName
------ ---- -----------
Running AeLookupSvc Application Experience
Running AppHostSvc Application Host Helper Service
Running AudioEndpointBu... Windows Audio Endpoint Builder
Running Audiosrv Windows Audio
Running BFE Base Filtering Engine

 このように、よく使用するフィルタ条件は、専用のフィルタを作成しておくと便利です。

 なお、フィルタはコンソール上で作成すると、PowerShell終了時に消滅してしまうので、プロファイルに登録することをお奨めします(連載第7回の「関数をプロファイルに追加する」参照)。

 関数とフィルタの違いをまとめると、次のとおりです。

  • 関数は、パイプで受け取ったオブジェクトを一度に処理する
  • フィルタは、パイプで受け取った個々のオブジェクトごとに実行する

スクリプトブロック

 「スクリプトブロック」とは、他の言語で言う匿名関数やラムダ式に相当するものです。PowerShellにおいては中括弧{}で囲まれたスクリプトコードに過ぎません。

 例えば

PS > 1..10 | foreach { Write-Host $_ }

 と記述したときの、{ Write-Host $_ }はスクリプトブロックです。

スクリプトブロックを変数に代入する

 スクリプトブロックは、変数に代入することもできます。

 例えば下記は、変数$aを3倍するというスクリプトブロックを変数$sに代入したことになります。

PS > $s = { $a * 3 }

スクリプトブロックを実行する

 では、スクリプトブロックを実行するにはどうすれば良いでしょうか?

PS > $s

 上記のように変数名を入力して[Enter]を押すだけでは実行することができません。

 スクリプトブロックを実行するには、下記のように &演算子を使用します。

PS > $a =2
PS > & $s
6

 スクリプトブロックではparamによる引数の受け取りが可能です。下記のスクリプトブロックは、paramで引数を1つ受け取り、引数を3倍します。

PS > $script = { param($a); $a * 3 }

 実行方法は、先ほど説明したように&演算子を使用して変数名を指定し、その後に引数として渡す値を記述します。

PS > & $script 5
15

 もう1つ、$argsによる値の受け取りも可能です。先ほどのスクリプトブロックを$argsで書き換えてみたのが下記です。

PS > $script = { $args[0] * 3 }

 実行方法は、paramのときと同様です。

PS > & $script 5
15

変数のスコープ

 まずは、下記スクリプトで変数のスコープについてみていきたいと思います。

Scope1.ps1
$a = 3

function Sample1
{
Write-Host "1)変数`$a=$a"
$a = 5
Write-Host "2)変数`$a=$a"
} #関数 sample1を実行
Sample1
Write-Host "3)変数`$a=$a"

 このスクリプトを実行すると結果は下記のようになります。

PS C:\Work> ./Scope1.ps1
1)変数$a=3
2)変数$a=5
3)変数$a=3

 この動作について図で解説します。

Windows PowerShell 入門(8)-関数編3

 スクリプトの1行目で変数$aを作成し、3を代入しています。この変数はスクリプト内で有効です(1行目から12行目までがこの変数のスコープ。スクリプトスコープという)。

 次に、11行目で関数Sample1を呼び出します。この呼び出しにより5行目が実行され、ここの変数$aでは1行目で代入した値「3」が表示されます。

 次に6行目で変数$aに「5」を代入していますが、この変数$aは1行目で作成した変数とは別のものです。関数Sample1内でのみ有効で、生存期間は6行目から7行目までとなります。これをローカル変数と呼びます。

 関数Sample1の実行が終わり、最後に12行目が実行されるわけですが、ここの変数$aは1行目で作成した変数$aであり、結果として「3」が表示されます。

 以上より、次のことが分かります。

  • 関数の外側で作成した変数は、スクリプトファイル内すべてが有効範囲である。
  • 関数内で作成した変数は、関数内のみが有効範囲である。
  • 関数の外で作成された変数と、関数内で作成した変数が同一名の場合は関数内で作成した変数が優先されるが、スクリプトスコープを持つ変数を上書きしない。

スクリプトスコープを持つ変数を参照する

 先ほどの例で、$aという同じ名前でスコープの異なる2つの変数を使用しました。関数内でローカル変数$aが優先されることは説明したとおりです。

 では、ローカル変数$aが優先されている状況下の中で、スクリプトスコープを持つ$aを参照するにはどうすれば良いでしょうか? この場合は、script修飾子を使用します。

 例を示します。

Scope2.ps1
$a = 3

function Sample2
{
Write-Host "1)変数`$a=$a"
$a = 5
Write-Host "2)変数`$a=$a"
Write-Host "3)変数`$a=$script:a"
$script:a = 7
Write-Host "4)変数`$a=$script:a"
Write-Host "5)変数`$a=$a"
} #関数 sample2を実行
Sample2
Write-Host "6)変数`$a=$a"

 このスクリプトの実行結果は下記の通りです。

PS C:\Work> ./Scope2.ps1
1)変数$a=3
2)変数$a=5
3)変数$a=3
4)変数$a=7
5)変数$a=5
6)変数$a=7

 このように変数名が重複する場合に、スクリプトスコープを持つ変数を参照するには、$script:aと、キーワードscriptを付加し、スクリプトスコープの変数への参照であることを明示します。

 また、スクリプトスコープの変数へ値を代入してもローカル変数の値は影響を受けないこと、関数を抜けた後の変数の値が変更されていることも確認してください。

 ポイントは次の2点です。

  • 関数内でスクリプトスコープを持つ変数へのアクセスはscript修飾子を使用する。
  • script修飾子を使用して値を書き換えても、同一名称のローカル変数へは影響しない。

 少し複雑なサンプルですが、ぜひ実際に実行して、ローカル変数とスクリプトスコープを持つ変数の違いを理解してください。

プライベートスコープ

 変数のスコープの種類には、プライベートスコープもあります。下記スクリプトで説明します。

Scope3.ps1
$a = 3

function funcA
{
Write-Host "2)$a"
$a = "7"
Write-Host "3)$a"
funcB
} function funcB
{
Write-Host "4)$a"
} Write-Host "1)$a"
funcA
Write-Host "5)$a"

 このスクリプトには2つの関数があり、最初にfuncAを呼び出し、funcAの中からfuncBを呼び出しています。またソース内での1)~5)は分かりやすいように実行順を示しています。

 このスクリプトの実行結果は下記の通りです。

PS C:\Work> ./Scope3.ps1
1)3
2)3
3)7
4)7
5)3

 このスクリプトの動作について図で説明します。

Windows PowerShell 入門(8)-関数編3

 1行目の変数$aはスクリプトスコープを持つ変数となるので、有効期間は赤線の通りでスクリプトファイルの最後までです。

 次に6行目の変数$aですが、この変数はローカル変数となりfuncAが有効期間です。

 さらに、この関数funcAfuncBを呼び出しており、funcAで作成したローカル変数$aの有効期間は青線で示した箇所、つまりfuncBまでおよびます。

 では、funcA内だけで有効なローカル変数を作成したい場合は、どうすれば良いでしょうか?

 これを実現するにはprivate修飾子を使用します。

 下記は「Scope3.ps1」をprivate修飾子を使用して書き換えたものです。funcAのローカル変数のスコープがfuncBに及ばないようにしています(6行目でpraivateキーワードを付加しています)。

Scope4.ps1
$a = 3

function funcA
{
Write-Host "2)$a"
$private:a = "7"
Write-Host "3)$a"
funcB
} function funcB
{
Write-Host "4)$a"
} Write-Host "1)$a"
funcA
Write-Host "5)$a"

 このスクリプトの実行結果は下記の通りです。

PS C:\Work> ./Scope4.ps1
1)3
2)3
3)7
4)3
5)3

 「Scope4.ps1」でのスコープを図で見てみましょう。

Windows PowerShell 入門(8)-関数編3

 privateキーワードを使用したので、funcAのローカル変数$aのスコープは青線の部分となります。

グローバルスコープ

 最後にグローバルスコープを持つ変数について説明したいと思います。グローバル変数は、スクリプトファイルを超えてスコープを持つ変数です。

 下記スクリプトで実験してみたいと思います。

Scope5.ps1
$global:glb_a = 3

function funcA
{
Write-Host "2)$glb_a"
} Write-Host "1)$glb_a"
funcA
Write-Host "3)$glb_a"

 グローバル変数を作成するには、1行目のようにglobal修飾子を使用します。

 このスクリプトの実行結果は下記の通りです。

PS C:\Work> ./scope5.ps1
1)3
2)3
3)3

 スクリプト内で$glb_aというグローバル変数を作成しました。最初に説明したように、グローバル変数はスクリプトファイルを超えてスコープを持つので、スクリプトの実行後にコマンドラインで変数を参照することが可能です。

PS C:\Work> $glb_a
3

 ではグローバル変数のスコープを図で見てみましょう。

Windows PowerShell 入門(8)-関数編3

 グローバル変数のスコープは、「Scope5.ps1」が呼び出された後(水色の矢印)スクリプトが終わるまで有効です。

 さらに、スクリプトが終了し、コンソールウィンドウに戻った後(赤の点線)もグローバル変数は生存し続けます。

 結果として、コマンドラインで$glb_aを確認できたというわけです。

まとめ

 今回は、次の3点について説明しました。

  • フィルタ
  • スクリプトブロック
  • 変数のスコープ

 変数のスコープは説明が長くなってしまいましたが、スクリプト開発を行う上で非常に重要な概念ですので、ぜひ覚えてもらいたいと思います。

 次回は、エラーの取り扱いについて説明します。