Powershell Invoke-Expression and System PATH behavior


Invoke-Expression is a language feature that allows for capturing the output of a command line application for parsing by your powershell script. If you are not careful, though, you could be plagued with messages like these:

Invoke-Expression : You must provide a value expression on the right-hand side of the '-' operator.
At C:\Users\Administrator\temp\someScript.ps1:3 char:29
+ $results = Invoke-Expression <<<<  $Action
    + CategoryInfo          : ParserError: (:) [Invoke-Expression], ParseException
    + FullyQualifiedErrorId : ExpectedValueExpression,Microsoft.PowerShell.Commands.InvokeExpressionCommand

 

 

 

For the last few months I've been using Invoke-Expression something like this:

$Action = "Certutil.exe –addstore –enterprise root $Certificate"
$result = Invoke-Expression $Action


This works fine if the EXE you are referencing is in one of the locations defined by the windows PATH Variable

 

If you attempt to run something like this, though, you will run into problems:

$Action = "c:\program files (x86)\nmap\nmap.exe -p 123 -sU -P0 time.windows.com"
$results = Invoke-Expression $Action

 

This code block returns the following error:

The term 'c:\program' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:11
+ c:\program <<<<  files (x86)\nmap\nmap.exe -p 123 -sU -P0 time.windows.com -oN c:\test.log --append-output
    + CategoryInfo          : ObjectNotFound: (c:\program:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

 

On the surface it seems fairly obvious what the next step should be: enclose the path to the exe in quotation marks taking care to escape them using the PowerShell back tick character ( ` ):

$Action = "`"c:\program files (x86)\nmap\nmap.exe`" -p 123 -sU -P0 time.windows.com"
$results = Invoke-Expression $Action

 

This puts us closer to our objective, but it still returns an error:

Invoke-Expression : You must provide a value expression on the right-hand side of the '-' operator.
At C:\Users\Administrator\someScript.ps1:3 char:29
+ $results = Invoke-Expression <<<<  $Action
    + CategoryInfo          : ParserError: (:) [Invoke-Expression], ParseException
    + FullyQualifiedErrorId : ExpectedValueExpression,Microsoft.PowerShell.Commands.InvokeExpressionCommand

 

This error baffled me for about an hour before I found out what needed to be done next: prefix the double-quoted path to the EXE with an ampersand like this:

$Action = "&`"c:\program files (x86)\nmap\nmap.exe`" -p 123 -sU -P0 time.windows.com"
$results = Invoke-Expression $Action

 

Without the ampersand ( & )  character, Powershell parses the expression as a string rather than as a command. In this case we have to be explicit and tell Powershell that there is a command in there that we want to run. While it makes for some gnarly syntax, it does the trick.

 

Addendum (14-July-2012): I recently ran into a problem that requires a slight alteration to the solution provided above.

 

Problem

If you have an executable that requires a quoted or double quoted parameter you may be surprised to note that this seemingly obvious syntax does not work:

iex "&`"C:\Program Files\Vendor\program.exe`" -i -pkg=`"Super Upgrade`" -usr=User -pwd=password2"

 

The above-syntax simply escapes the double quotes around the parameter so they should be passed along to the EXE. Unfortunately you get this error:

powershell_error.png


Invoke-Expression : The string starting:
At line:1 char:62
+ &"C:\Program Files\Vendor\program.exe" -i -pkg="Super Upgrade <<<< " -usr=User -pwd=password2
is missing the terminator: ".
At line:1 char:4
+ iex <<<<  "&`"C:\Program Files\Vendor\program.exe`" -i -pkg=`"Super Upgrade`" -usr=User -pwd=password2"
    + CategoryInfo          : ParserError: ( -usr=User -pwd=password2:String) [Invoke-Expression], IncompleteParseException
    + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString,Microsoft.PowerShell.Commands.InvokeExpressionCommand

 

Solution

The Baffling solution in my case was to triple-escape the double-quoted parameter. What makes this so confusing is that I only have to triple escape the closing quote:

iex "&`"C:\Program Files\Vendor\program.exe`" -i -pkg=`"Super Upgrade```" -usr=User -pwd=password2"

 

YMMV