PowerShell 文字列をコマンドとして実行する Invoke-Expression
1.はじめに
実行したいコマンドレットを文字列として作り込んでから実行する方法を調べてみた。
2.環境
PowerShellのバージョンは以下の通り。
PS > $PSVersionTable Name Value ---- ----- PSVersion 5.1.18362.752 ・・・以下、略・・・
3.目指すところ
- Invoke-Expression の使い方を覚える
4.やってみよう
4.1 Invoke-Expression
Invoke-Expression何するものぞ
Invoke-Expressionは文字列をコマンドとして実行するコマンドレット。例を見れば早い。
PS > $cmd = "Get-Alias" ・・・"Get-Alias"という文字列を変数に格納 PS > Invoke-Expression $cmd ・・・文字列をコマンドとして実行 CommandType Name Version Source ----------- ---- ------- ------ Alias % -> ForEach-Object Alias ? -> Where-Object Alias ac -> Add-Content Alias asnp -> Add-PSSnapin (略)
実行したいことが条件によって微妙に異なる場合は多い。そんなときは、実行したいことを文字列として作り込んでから実行すればいい。
Invoke-Expressionを使わない例
簡単な例として、偶数なら「実動作がGet-ChildItemとなっているエイリアスを列挙」、奇数なら「lsのとして定義されているエイリアスを表示」するスクリプトを作成してみた。どちらもGet-Aliasコマンドレットを使って実現できるが、Invoke-Expressionを使わないと、以下のようになる。
# $joken:0~1 $joken = (Get-Random) % 2 if($joken -eq 0){ #偶数 Get-Alias -Definition Get-ChildItem }else{ #奇数 Get-Alias -Name ls }
Invoke-Expressionを使った例
同じことをInvoke-Expressionを使って書いてみる。
# $joken:0~1 $joken = (Get-Random) % 2 $cmd = "Get-Alias " if($joken -eq 0){ #偶数 $cmd += "-Definition Get-ChildItem" }else{ #奇数 $cmd += "-Name ls" } Invoke-Expression $cmd
あまりにもシンプルな例なので「こんなの意味あるの?」と思うかもしれないが、少し複雑なスクリプトになると地味に便利なことに気づくはず。
Invoke-Expressionの注意点(つまづきやすい点)
配列変数を文字列として扱うには注意が必要。 a[0] = 1, a[1] = 2, a[2] = 3という配列をカンマ区切りで表示する例で示す。
まずは、配列を作って単純に表示させると、こんな感じ。
PS > $a = @(1,2,3) PS > Write-Host $a 1 2 3
上記配列をカンマ区切りで表示させると、こんな感じ。
PS > Write-Host ($a -join ',') 1,2,3
では、同じことを文字列として実行してみよう。
PS > $cmd = "Write-Host ($a -join ',')" PS > Invoke-Expression $cmd Invoke-Expression : 発生場所 行:1 文字:15 + Write-Host (1 2 3 -join ',') + ~ 式またはステートメントのトークン '2' を使用できません。 発生場所 行:1 文字:14 + Write-Host (1 2 3 -join ',') + ~ 式の終わりの ')' が存在しません。 発生場所 行:1 文字:28 + Write-Host (1 2 3 -join ',') + ~ 式またはステートメントのトークン ')' を使用できません。 発生場所 行:1 文字:1 + Invoke-Expression $cmd + ~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ParserError: (:) [Invoke-Expression], ParseException + FullyQualifiedErrorId : UnexpectedToken,Microsoft.PowerShell.Commands.InvokeExpressionCommand
エラーになる。エラー内容を見てみると、実行されているのは Write-Host (1 2 3 -join ',') というコマンドのようだ。これではうまくいかないのも当然。
以下をみてもらえばわかるが、$cmdという変数に文字列を格納した時点で配列が展開されてしまっている。
PS > $cmd = "Write-Host ($a -join ',')" PS > Write-Host $cmd Write-Host (1 2 3 -join ',')
これを回避するには、以下のように配列変数部分を`(バッククォータ)でくくる。バッククォータはキーボードの@マークが書かれているキーをShift押しながら入力。
PS > $cmd = "Write-Host (`$a` -join ',')" PS > Write-Host $cmd Write-Host ($a -join ',') PS > Invoke-Expression $cmd 1,2,3
ということで、配列変数を文字列にしたいときには注意が必要ということは覚えておきたい。
終.獲得した知識
- Invoke-Expressionコマンドレットの使い方
- 定義済み配列を文字列の中で展開させない方法