1

I have been doing a lot of reading on invoke-expression (also known as iex) and I'm having trouble getting it to work for me.

My understanding is, it will run any powershell code you give to it. However, when I run my tests on it, it does not run the code.

Example:

## testcode.ps1

$myvar = "i am here"
if ($myvar -ne $null) {
    "($myvar) variable is Full"
} else {
    "($myvar) variable is Empty"
}

Now, if I cat(gc) this file and I pass it to iex, it outputs a bunch of errors. Same thing happens when I save the code into a variable and then feed the variable to iex. Neither works.

Despite the fact that I've tried numerous examples, I feel there's something minor I'm doing wrong that I'm hoping someone can point out for me.

I'm new to Windows scripting, so please bear with me. These are the results of the tests I performed:

First Test:

PS C:\Users\J> gc C:\Users\J\testcode.ps1 | iex
Invoke-Expression : Cannot bind argument to parameter 'Command' because it is an empty string.
At line:1 char:31
+ cat C:\Users\J\testcode.ps1 | iex    
+                               ~~~    
    + CategoryInfo          : InvalidData: (:PSObject) [Invoke-Expression], ParameterBindingValidationException   
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.InvokeExpressionCommand
iex : At line:1 char:23
+ if ($myvar -ne $null) {
+                       ~
Missing closing '}' in statement block or type definition.
At line:1 char:31
+ cat C:\Users\J\testcode.ps1 | iex
+                               ~~~
    + CategoryInfo          : ParserError: (:) [Invoke-Expression], ParseException
    + FullyQualifiedErrorId : MissingEndCurlyBrace,Microsoft.PowerShell.Commands.InvokeExpressionCommand

Second Test:

PS C:\Users\J> $scriptBlock = gc C:\Users\J\testcode.ps1
PS C:\Users\J> 
PS C:\Users\J> iex -Command "$scriptBlock"
iex : At line:1 char:23
+  $myvar = "i am here" if ($myvar -ne $null) {     "($myvar) variable  ...
+                       ~~
Unexpected token 'if' in expression or statement.
At line:1 char:1
+ iex -Command "$scriptBlock"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ParserError: (:) [Invoke-Expression], ParseException
    + FullyQualifiedErrorId : UnexpectedToken,Microsoft.PowerShell.Commands.InvokeExpressionCommand

PS C:\Users\J>

I'm aware that I can just run the file containing the code. However, I need help figuring out how iex works and what it is I'm doing wrong.

Please kindly advise.

KLZY
  • 83
  • 2
  • 8
  • Well the file running the code won't work with `Get-Content` since it goes line by line without separators when put like that. The script also won't receive output into a pipeline since you only wrote the output to the host instead of returning it. Use `return "($myvar) variable is Full"` and the other one. you can use `Get-Content` if you use a `foreach` loop and add each line to a variable separated by a newline to a variable that you then converted to a script block – Nico Nekoru Jun 22 '20 at 21:34
  • thanks for the comment. but i have no idea how to do the for loop part you mentioned. seems like a roundabout way though. @Bill_Stewart suggestion looks promising but fails for me. – KLZY Jun 22 '20 at 21:44
  • @Bill_Stewart i tried `[scriptblock]::Create("$scriptBlock") | iex` and i still get errors. I tried it without the quotes a well and it still failed. – KLZY Jun 22 '20 at 21:46
  • 2
    As @mklement0's answer demonstrates, `Get-Content`'s `-raw` switch will read the whole file instead of line by line like I stated in my comment. – Nico Nekoru Jun 22 '20 at 22:05
  • Thanks, @NekoMusume, but note that `return` is never _necessary_: any statement whose output is neither captured nor redirected is _implicitly_ output (returned) to the _success output stream_, which by default prints to the host (console). – mklement0 Jun 22 '20 at 23:10

3 Answers3

1

First things first:

Invoke-Expression should generally be avoided and used only as a last resort, due to its security risks. In short: avoid it, if possible, given that 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.

For the record: in the case at hand, the superior alternative is to directly invoke the script file:

# Prepend `& `, if the script file path is quoted or references a variable.
C:\Users\J\testcode.ps1

Invoke-Expression (iex) accepts multiple strings via the pipeline, and evaluates each individually, as a self-contained script.
Therefore, you must provide the contents of your script as a whole, as a single string, which is what Get-Content's (gc's) -Raw switch does[1]:

Get-Content -Raw C:\Users\J\testcode.ps1 | Invoke-Expression

Alternatively, pass the script-file contents as an argument:

Invoke-Expression (Get-Content -Raw C:\Users\J\testcode.ps1)

Note that passing the string to evaluate as an argument truly only accepts a single string, so the command would fail without -Raw.


[1] By default, the Get-Content cmdlet reads a file line by line, passing each line through the pipeline as it is being read.

mklement0
  • 382,024
  • 64
  • 607
  • 775
0
$myvar = "I'm Here"

#Using Invoke-Expression - Accepts a STRING as Input

$SBCode = 'if ($Null -ne $myvar) {"($myvar) variable is Full"}' +
          'else {"`$myvar variable is Empty"}'

Clear-Host
"Before Invoke-Expression `$myvar = $myvar"

$Result = Invoke-Expression  $SBCode

"Invoke-Expression Returns: $Result"

#Using Invoke-Command - Accepts Script Block as Input

$SBCode = {
   if ($myvar -ne $null) {
     "($myvar) variable is Full"
   } 
   else {
    "`$myvar variable is Empty"
   }
} #End $SBCode Script Block

"Before Invoke-Command `$myvar = $myvar"

$Result = Invoke-Command -ScriptBlock $SBCode

"Invoke-Command Returns: $Result"

Results:

Before Invoke-Expression $myvar = I'm Here
Invoke-Expression Returns: (I'm Here) variable is Full
Before Invoke-Command $myvar = I'm Here
Invoke-Command Returns: (I'm Here) variable is Full

# After changing $MyVar = $Null

Before Invoke-Expression $myvar = 
Invoke-Expression Returns: $myvar variable is Empty
Before Invoke-Command $myvar = 
Invoke-Command Returns: $myvar variable is Empty

HTH

RetiredGeek
  • 2,980
  • 1
  • 7
  • 21
0

You can use out-string to convert output into string.

cat C:\Users\J\testcode.ps1 | out-string | Invoke-Expression

Hunger
  • 5,186
  • 5
  • 23
  • 29