PowerShell 第5回 他のPowerShell Scriptを実行

#はじめに
今回は、他のPowerShellのScriptを実行するPowerShell Scriptを作成します。
PowerShellでいくつか簡単なツールを作ります。
ツールAを実行して、ツールBを実行するみたいに、複数のツールをシーケンシャルに実行したくなります。
ということで、調べてみるとできそうということなので、サンプルを備忘としてまとめます。

#今回実施する内容
今回は、参照元のScriptと参照先のScript(RefferredScript.ps1)を作成し、参照元から参照先のScriptを参照します。
実行方法を２つ見つけたのでその２つで実施します。
なお、２つのScriptは同階層に存在するものとします。

・.（ドット）を使用して実現
・Invoke-Expressionを使用して実現

#ソースコード(Git Hub)
[PowerShell_05_ReferredScript](https://github.com/Build-Mount/PowerShell_05_ReferredScript)

#環境
OS: Windows 10 JP (64bit)
PowerShell version: 5.1.19041.1

#参考
なし

#用語

#.（ドット）を使用して実現
##シンプルな関数の読み込み
|Script|説明|
|:--|:--|
|BaseScript.ps1|参照元Script。参照先Scriptの関数を呼び出す。|
|ReferredScript.ps1|参照先Script。ターミナルに文字列を出力する関数だけ定義。|

参照元Scriptから参照先Scriptの関数を呼び出して実行します。
単に文字列をターミナルに出力するだけです。

##ソースコード

```PowerShell:BaseScript.ps1
try {
    . .\referredScript.ps1
    ShowMessage("参照元Scriptで、参照先Scriptの関数を参照して実行します。")
} catch {
    Write-Host $Error[0]
}
```

```PowerSHell:ReferredScript.ps1
function ShowMessage {
	[CmdletBinding()]
	param (
		[Parameter(Mandatory = $true)]
		[string]$Message
	)
	Write-Host $Message
}
```

```
＜実行結果＞
参照元Scriptの関数を参照して実行します。
```

##ソースコードの説明
２つのScriptは同じ階層に置きます。
参照先Scriptは単にWrite-Hostで$Messageをターミナルに表示するだけのシンプルな関数です。
参照元Scriptでは、
``. .\referredScript.ps1``
で、referredScript.ps1を実行します。といっても実行文はなく関数記載だけのため、関数の読み込みだけ実施されます。
その後``ShowMessage``で、参照元Scriptから参照先Scriptの関数を実行します。

##.（ドット）の説明
これでいけるということですね。最初の**「.」**がなじみがなかったので調べてみると、
[PowerShell用語集](https://docs.microsoft.com/ja-jp/powershell/scripting/learn/glossary?view=powershell-7.1)

>|期間|定義|
|:--|:--|
ドット ソース|	PowerShell では、コマンドの前にドットとスペースを入力することによってコマンドを開始すること。 ドット ソースのコマンドは、新しいスコープではなく、現在のスコープで実行されます。 コマンドが作成するすべての変数、エイリアス、関数、またはドライブは、現在のスコープで作成され、コマンドが完了すると使用できるようになります。|

なるほど。これでコマンド開始することになるんですね。そしてスコープは現在のスコープと。
「期間」は単なる翻訳のミスですね。ただしくは「Term」なので「用語」ですね。

##参照先Scriptで実行文がある場合の動作
実際に使用するとなると、最初に試したようなライブラリ的な使い方もすると思いますが、別のツール(別のScript)を実行するという使い方もすると思います。
ということで、参照先Scriptに実行文を入れた場合の動作を試します。

|Script|説明|
|:--|:--|
|BaseScript2.ps1|参照元Script。参照先Scriptの関数を呼び出す。|
|ReferredScript2.ps1|参照先Script。ターミナルに文字列を出力する関数を定義。加えてその関数を実行|

##ソースコード

```PowerShell:BaseScript2.ps1
try {
    . .\referredScript2.ps1
    ShowMessage("参照元Scriptで、参照先Scriptの関数を参照して実行します。")
} catch {
    Write-Host $Error[0]
}
```

```PowerShell:ReferredScript2.ps1
function ShowMessage {
	[CmdletBinding()]
	param (
		[Parameter(Mandatory = $true)]
		[string]$Message
	)
	Write-Host $Message
}

ShowMessage("参照先Scriptです。ShowMessageを実行します。")
```

```
＜実行結果＞
参照先Scriptです。ShowMessageを実行します。
参照元Scriptの関数を参照して実行します。
```

##ソースコードの説明
これを実行すると参照先Scriptの実行文が実行された後、参照元Scriptの読み込んだShowMessageが実行されるということです。
結局両方実行されるってことですね。
これなら、単体で参照先Scriptを使ってもよいし、参照元Scriptから読み込んで使ってもよいし、期待通りです。

#Invoke-Expressionを使用して実現
##シンプルな関数の読み込み
|Script|説明|
|:--|:--|
|BaseScript3.ps1|参照元Script。参照先Scriptの関数を呼び出す。|
|ReferredScript3.ps1|参照先Script。ターミナルに文字列を出力する関数だけ定義。|

参照元Scriptから参照先Scriptの関数を呼び出して実行します。
単に文字列をターミナルに出力するだけです。

##ソースコード

```PowerShell:BaseScript3.ps1
try {
    Invoke-Expression .\referredScript3.ps1
    ShowMessage("参照元Scriptで、参照先Scriptの関数を参照して実行します。")
} catch {
    Write-Host $Error[0]
}
```

```PowerSHell:ReferredScript3.ps1
function ShowMessage {
	[CmdletBinding()]
	param (
		[Parameter(Mandatory = $true)]
		[string]$Message
	)
	Write-Host $Message
}
```

```
＜実行結果＞
参照元Scriptの関数を参照して実行します。
```

##Invoke-Expressionの説明
「.（ドット）を使用して実現」と結果は同じです。
動作は似たようなものかなと思いますが、Powershelのドキュメント[Invoke-Expression](https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.utility/invoke-expression?view=powershell-7.1)から引用すると、以下です。

>###Example 2: Run a script on the local computer
>```PowerShell
>Invoke-Expression -Command "C:\ps-test\testscript.ps1"
>"C:\ps-test\testscript.ps1" | Invoke-Expression
>```
>
>These commands use ``Invoke-Expression`` to run a script, TestScript.ps1, on the local computer. The two commands are equivalent. The first uses the **Command** parameter to specify the command to run. The second uses a pipeline operator (``|``) to send the command string to ``Invoke-Expression``.

コマンドを開始するといっているのでやはり同じようです。パイプラインも使えるようです。

##参照先Scriptで実行文がある場合の動作
「.（ドット）を使用して実現」と同様に、参照先Scriptに実行文を入れた場合の動作を試します。

|Script|説明|
|:--|:--|
|BaseScript4.ps1|参照元Script。参照先Scriptの関数を呼び出す。|
|ReferredScript4.ps1|参照先Script。ターミナルに文字列を出力する関数を定義。加えてその関数を実行|

##ソースコード

```PowerShell:BaseScript4.ps1
try {
    Invoke-Expression .\referredScript4.ps1
    ShowMessage("参照元Scriptで、参照先Scriptの関数を参照して実行します。")
} catch {
    Write-Host $Error[0]
}
```

```PowerShell:ReferredScript4.ps1
function ShowMessage {
	[CmdletBinding()]
	param (
		[Parameter(Mandatory = $true)]
		[string]$Message
	)
	Write-Host $Message
}

ShowMessage("参照先Scriptです。ShowMessageを実行します。")
```

```
＜実行結果＞
参照先Scriptです。ShowMessageを実行します。
参照元Scriptの関数を参照して実行します。
```

##ソースコードの説明
これを実行すると参照先Scriptの実行文が実行された後、参照元Scriptの読み込んだShowMessageが実行されるということです。
結局両方実行されるってことですね。
結果は「.（ドット）を使用して実現」と同じですね。

##参照先Scriptで実行文があって結果を参照元Scriptに戻す動作
参照元Scriptで参照先Scriptを呼び出す場合、その処理が正常に終わったかどうか参照したくなります。
例えば、ツールAを実行して正常に終了したならば、ツールBを実行というように、正常かどうかを示す戻り値を返したくなります。
ということで試してみます。

|Script|説明|
|:--|:--|
|BaseScript5.ps1|参照元Script。参照先Scriptの関数を呼び出す。|
|ReferredScript5.ps1|参照先Script。ターミナルに文字列を出力する関数を定義。加えてその関数を実行|

##ソースコード

```PowerShell:BaseScript5.ps1
try {
    $result = Invoke-Expression .\ReferredScript5.ps1
    if ($result -eq 1) {
        ShowMessage("参照元Scriptの関数の戻り値を取得成功。")
    } else {
        ShowMessage("参照元Scriptの関数の戻り値を取得失敗。")
    }
} catch {
    Write-Host $Error[0]
}
```

```PowerShell:ReferredScript5.ps1
function ShowMessage {
	[CmdletBinding()]
	param (
		[Parameter(Mandatory = $true)]
		[string]$Message
	)
	Write-Host $Message
    1
}

ShowMessage("参照先Scriptです。ShowMessageを実行します。")
```

```
＜実行結果＞
参照先Scriptです。ShowMessageを実行します。
参照元Scriptの関数の戻り値を取得成功。
```

##ソースコードの説明
PowerShell:BaseScript5.ps1では、ReferredScript5.ps1の実行文の戻り値を``$result``に代入して、その値が1か判断し、ターミナルに出力します。
ReferredScript5.ps1では、ShowMessage関数内で、1を出力します。
ということで、戻り値1を$resultに代入ができて、それに応じた動作を実行できました。
期待通りです。

#おわりに
今回は、他のPowerShellのScriptを実行するPowerShell Scriptを作成し、動作を確認しました。
期待通りの動作ができて満足です。
Invoke-Expressionのほうが、明示的でわかりやすいかなと思いました。
.（ドット）だと、戻り値がどうやって帰ってくるのかわからなかったですし。
これをベースにツールを複数実行するScriptを試してみることにします。
何かあればまた記事更新します。