1

I am running this script

Invoke-Expression $expression -ErrorAction SilentlyContinue

The variable $expression may not have a value sometimes. When It's empty, I get the error

Cannot bind argument to parameter 'Command' because it is null.

How can I avoid seeing the error? I want to execute Invoke-Expression regardless of $expression being empty, so an if statement checking that $expression has a value wouldn't work.

enter image description here

FranLegon
  • 87
  • 1
  • 2
  • 9
  • 1
    Have u tryed try{} catch{} yet? – Dom Jun 02 '23 at 15:26
  • 1
    this is expected behavior there is no "action" if the cmdlet doesn't even start. `function foo {[CmdletBinding()]param([ValidateNotNull()] $i) }; foo $null -ErrorAction SilentlyContinue` use a try / catch block – Santiago Squarzon Jun 02 '23 at 15:26
  • 2
    You may cheat by appending a space: `Invoke-Expression "$expression "` – zett42 Jun 02 '23 at 16:01
  • @zett42 that worked! Thank you very much. You can post it as an answer If you'd like me to accept it as solution – FranLegon Jun 02 '23 at 16:41

2 Answers2

2

How can I avoid seeing the error?

By not passing a $null value to Invoke-Expression:

if (-not [string]::IsNullOrEmpty($expression)) {
  Invoke-Expression $expression -ErrorAction SilentlyContinue
}

If you insist on invoking Invoke-Expression, wrap the input string in a dot-sourced scriptblock - that way Invoke-Expression never receives empty input:

Invoke-Expression ".{${expression}}" -ErrorAction SilentlyContinue
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • As I said, `I want to execute Invoke-Expression regardless of $expression being empty, so an if statement checking that $expression has a value wouldn't work.` – FranLegon Jun 02 '23 at 16:39
  • 1
    @FranLegon You can't, but you can wrap it in something non-empty, answer updated – Mathias R. Jessen Jun 02 '23 at 17:05
2

First, the obligatory warning:

  • Invoke-Expression (iex) should generally be avoided and used only as a last resort, due to its inherent security risks. Superior alternatives are usually available. If there truly is no alternative, only ever use it on input you either provided yourself or fully trust - see this answer.

To add to Mathias' helpful answer and zett42's simpler alternative mentioned in a comment on the question (Invoke-Expression "$expression "):

These solutions silence only the case where $null or an empty string is passed to Invoke-Expression - which may well be your intent.

To also cover the case where all error output should be silenced - whether due to invalid input or due to valid input causing errors during execution - the following variation is needed:

# Silences *all* errors.
try { Invoke-Expression $expression 2>$null } catch { }

# Alternative:
# Silences *all* errors and additionally *ignores* all *non-terminating* errors, 
# i.e. not only silences them, but also prevents their recording in $Error.
# By executing inside & { ... }, the effect of setting $ErrorActionPreference is 
# transitory due to executing in a *child scope*.
# Note that this also means that $expression is evaluated in the child scope.
& { $ErrorActionPreference = 'Ignore'; Invoke-Expression $expression }

Note:

  • First command:

    • The common -ErrorAction parameter fundamentally only acts on non-terminating errors, whereas terminating ones (both statement- and script-terminating ones) must be handled with a try / catch / finally statement.

      • Passing $null or the empty string to Invoke-Expression causes an error during parameter binding (that is the, cmdlet itself is never invoked, because invalid arguments were passed), which in effect is a statement-terminating error - hence the need for try / catch.

      • The try / catch with the empty catch block additionally prevents script-terminating errors that result from the Invoke-Expression call from terminating your script too (e.g, if $expression contained something like 'throw "Fatal Error"'

    • Note that -ErrorAction SilentlyContinue was replaced with 2>$null in order to silence non-terminating errors (e.g., the error resulting from Get-ChildItem NoSuchDir), because - inexplicably - -ErrorAction is not effective with Invoke-Expression (even though the -ErrorVariable common parameter does work, for instance). See GitHub issue #19734.

  • Second command:

    • Setting the $ErrorActionPreference preference variable to Ignore causes all errors to be silenced, and additionally - for non-terminating errors only - prevents their recording in the automatic $Error variable.

      • If -ErrorAction worked in this case, -ErrorAction would have the same effect, but would act solely on non-terminating errors, as noted. (This asymmetry between what should be equivalent mechanisms - preference variable vs. per-call common parameter, is one of the pitfalls of PowerShell's error handling - see GitHub issue #14819).
    • Unfortunately, (caught) terminating errors are invariably recorded in $Error as of PowerShell 7.3.4. From what I can tell, changing this in a future version has been green-lit a while ago, but is yet to be implemented: see GitHub issue #3768

    • Using &, the call operator with a script block { ... } executes the enclosed statements in a child scope.

    • This causes $ErrorActionPreference = 'Ignore' to create a local copy of the preference variable, which automatically goes out of scope when the script block is exited, thereby implicitly restoring the previous value of $ErrorActionPreference.

    • However, this also means that the code executed by Invoke-Expression executes in that child scope.
      If that is undesired, forgo the & { ... } enclosure and save and restore the previous $ErrorActionPreference value.

mklement0
  • 382,024
  • 64
  • 607
  • 775