Skip to content
shellmap

invoke-expressionExecute a string as PowerShell — eval equivalent across all 5 shells

Equivalents in every shell

Bashunix
eval "$cmd_string"

Builtin. Parses the argument as a shell command and runs it in the CURRENT shell — it sees and can modify the shell's variables and functions. `eval "$user_input"` is the canonical injection vector and an OWASP regular.

Zshunix
eval "$cmd_string"

Same `eval` builtin. Zsh adds `noglob eval "$x"` to suppress globbing during parsing — useful when the string contains literal `*` you do not want expanded.

Fishunix
eval "$cmd_string"

Builtin in fish 3+ (replaced the earlier `function eval` shim). Same injection-risk profile as bash. Fish style guides recommend `string` builtins plus `command` invocation over `eval` for parsed input.

PowerShellwindows
Invoke-Expression $cmdString

Aliased as `iex`. Parses the input string as PowerShell script and executes it in the CURRENT scope — it sees and modifies session variables and functions. Microsoft explicitly recommends AVOIDING it for any input you do not fully control.

cmd.exewindows
call %cmdVar%

`call %var%` re-parses variable substitution to allow nested-variable expansion (limited eval-like behaviour). For full dynamic execution, write the string to a `.bat` file and invoke it. cmd has no general `eval` builtin — by design.

Worked examples

Execute a dynamically constructed command

Bash
cmd="ls -la /tmp"; eval "$cmd"
PowerShell
$cmd = "Get-ChildItem -Force C:\Windows"; Invoke-Expression $cmd

Run the output of another command as code (typical bootstrapper anti-pattern)

Bash
eval "$(ssh-agent -s)"
PowerShell
Invoke-Expression (Invoke-WebRequest https://example.com/install.ps1).Content

Safer alternative: use the call operator with arguments as an array (no parsing)

Bash
cmd=(ls -la /tmp); "${cmd[@]}"
PowerShell
$exe = "ls"; $args = @("-la", "/tmp"); & $exe @args

Gotchas

  • `Invoke-Expression $userInput` is the canonical PowerShell code-injection vector. Microsoft explicitly recommends against passing untrusted input to it. Safer alternatives for almost every use case: `&` (call operator) with separate args, splatting (`& $exe @paramHashtable`), `[scriptblock]::Create($x).Invoke()` (slightly safer because invocation is explicit), or `Get-Command` resolution.
  • Scope: `Invoke-Expression` runs in the CALLER'S scope — assignments persist after it returns. `Invoke-Expression '$x = 42'` sets `$x` in your current scope. This makes it dangerous AND useful (e.g. for sourcing config files that set variables).
  • Quoting is fragile: `Invoke-Expression "Get-Process -Name $name"` interpolates `$name` BEFORE invocation, so a `$name` of `'foo; Remove-Item *'` runs the destructive part. Single-quote and concat to defer expansion: `Invoke-Expression ('Get-Process -Name ' + $name)` — still dangerous if `$name` is untrusted, but at least the surrounding text is fixed.
  • PSScriptAnalyzer flags every `Invoke-Expression` usage as `PSAvoidUsingInvokeExpression` — a code-review smell signal. Legitimate uses (REPL-like tools, package bootstrappers) should suppress with `[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression','')]` AND document the justification in a comment immediately above the call.
  • Performance: `Invoke-Expression` parses the input string on EVERY call — far slower than calling a cmdlet or function directly. Inside loops, parse once via `$sb = [scriptblock]::Create($code); 1..1000 | ForEach-Object { & $sb }` to amortise parsing.

WSL & PowerShell Core notes

pwsh`Invoke-Expression` is identical on every pwsh platform; the security guidance is identical too — avoid for untrusted input everywhere. The slightly-safer alternative on all platforms is `[scriptblock]::Create($code)`, which requires an explicit `.Invoke()` and produces a discrete scriptblock that scope-isolates cleanly when called via `Invoke-Command -ScriptBlock {...} -ArgumentList @(...)`.
WSLInside WSL bash, `eval "$cmd"` is the bash-native form. Calling pwsh `Invoke-Expression` from WSL: `pwsh.exe -c "Invoke-Expression \"Get-Process\""` works but nested-quoting is a known footgun — prefer writing the script to a `.ps1` file and invoking `pwsh.exe -File ./script.ps1`. From Windows-side pwsh, `Invoke-Expression "wsl ls -la"` works for one-shot commands but `wsl bash -c 'eval $cmd'` is cleaner when the dynamic part lives in a bash variable.

Related commands