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:
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