Welcome to the Inedo Forums! Check out the Forums Guide for help getting started.
If you are experiencing any issues with the forum software, please visit the Contact Form on our website and let us know!
Suggestion: allow Execute-Powershell to return output stream and/or capture output variables
-
There does not appear to be a means to capture the output stream from
Execute-Powershell
, nor does it appear possible to obtain the value of a variable set inside the Powershell script back to the enclosing OtterScript.It is possible to get a result back from
$PSEval()
but the options to both$PSEval
andExecute-Powershell
are not equivalent. For instance:Execute-Powershell
uses aText
property which does not interpolate OtterScript variables at the script level (it extracts and dispatches them), which means it better handles the$variables
that are specific to the Powershell script$PSEval()
does interpolate OtterScript variables, which means all the variables in the Powershell script have to be backtick-escaped, whether they are specific to the Powershell script or defined in the OtterScript context$PSEval()
does not like parentheses; many of these have to be escaped as well, and it is not always clear which ones$PSEval()
does not like newlines; these can be within "swim" strings, but these still require escaping at least the variables$PSEval()
is not (currently) particularly supportive of scripts with varying output (see #4920).
Correctly escaping all the parentheses and variables in any Powershell longer than a couple of lines is an exercise in torture.
Execute-Powershell
is clearly the better choice for more complex scripts, but seems to lack the means to return anything back to the caller.As such, could
Execute-Powershell
be at least augmented with an output parameter, to capture anything in the Powershell output stream back to target variable? (noting it should be made aware of scalar, vector or map context)Alternatively/additionally, it would be useful for
Execute-Powershell
to export variables back to the calling OtterScript context. Clobbering existing variables across the board might not be the right approach (so as not to break existing scripts), but perhapsExecute-Powershell
could be given an optional input parameter which is a list of variable names to export, so the capture is opt-in?(I assume this was the intent of including them in ExecutePowerShellJob+Result, and that
Result
is correctly populated...)# context aware output.. Execute-Powershell ( Text: >> Write-Output "abc" >>, OutputVariable => $foo ); Execute-Powershell ( Text: >> Write-Output "abc" Write-Output "def" Write-Output "ghi" >>, OutputVariable => @bar ); Execute-Powershell ( Text: >> Write-Output @{a = 1; b = 2; c = 3} >>, OutputVariable => %baz ); # or, with capture... Execute-Powershell ( Text: >> $a = 10 + 5 $b = @($a, 10) $c = @{a = $a; b = $b} >>, CaptureVariables: @(a, b, c) ); Log-Information $a; # -> 15 Log-Information $ToJson(@b); # -> ["15", "10"] Log-Information $ToJson(%c); # -> { "a": "15", "b": ["15", "10"] }
PS: various crimes against humanity, trying to escape properly are below, to demonstrate the difficulties...
set $foo = "hello 'world'"; set @bar = @(); # 'natural' approach, newlines, no escapes: (otter compile error: 'Expected ;') { set @a = @PSEval( for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) } ); Log-Information `@a: $ToJson(@a); } # flatten newlines, no escapes: (otter runtime error: 'cannot resolve variable $i') set @a = @PSEval(for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) }); # flatten newlines, no escapes: (otter runtime error: 'invalid use of vector expression in scalar') set $a = $PSEval(for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) }); # flatten newlines, escape parens: (otter runtime error: 'cannot resolve variable $i') set @a = @PSEval(for `($i = 1; $i -le 5; $i++`) { Write-Output `('{0} {1}' -f $foo,$i`) }); # flatten newlines, escape Powershell sigils and parens: ($foo is interpolated, not captured; Powershell syntax error at '-f hello') set @a = @PSEval(for `(`$i = 1; `$i -le 5; `$i++`) { Write-Output `('{0} {1}' -f $foo,`$i`) }); # flatten newlines, escape all sigils and parens: ($foo is captured; powershell runtime error: 'missing closing ")"') set @a = @PSEval(for (`$i = 1; `$i -le 5; `$i++`) { Write-Output ('{0} {1}' -f `$foo,`$i`) }); # flatten newlines, escape all sigils and parens: ($foo is captured; powershell runtime error: 'missing closing ")"') set @a = @PSEval(for `(`$i = 1; `$i -le 5; `$i++`) { Write-Output `('{0`} {1`}' -f `$foo,`$i`) }); # as a swim string; same as above: ('cannot resolve $i' when unescaped; 'missing closing ")"' when escaped) set @a = @PSEval(>-|> for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) } >-|>); # as a variable, loaded by swim string; escaping all sigils: (captures $foo, this is the first one that works) set $ps = >-|> for (`$i = 1; `$i -le 5; `$i++) { Write-Output ('{0} {1}' -f `$foo,`$i) } >-|>; set @a = @PSEval($ps); # executes with the 'natural' syntactic approach; captures $foo but has no means to return output Execute-Powershell( Text: >-|> for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) } >-|> ); # similarly 'natural'; captures $foo and $bar, but does not populate @bar Execute-Powershell( Text: >-|> $bar = @() for ($i = 1; $i -le 5; $i++) { $bar += ('{0} {1}' -f $foo,$i) } >-|> );
(Reposted from Github on request)
-
@jimbobmcgee thanks for reposting this here as well!
PSEval
is definitely not meant for scripts like that, due to how the parsing works... but as you noticed, the$PSEval($ps)
should work. We probably won't change this.PSExec
(i.e.Execute-Powershell
) can capture variables, but not output streams. So something like this:set $hello = world; $PSExec >> $hello = 'dears'; >>; Log-Information Hello $hello;
Similar to my comments on the PSEval thread, this is another one of those rabbitholes that can break stuff, since the existing behavior seems to work for some users. So we're super-cautious about it.
It's likely we won't change these behaviors as they are "good enough" for the intended usecase of Otter.
-
@dean-houston I hadn't noticed
Execute-Powershell
automatically capturing output variables. I'll do some more tests and see if it works for me.This is probably a reasonable candidate for the
/literal
proposal described in the documentation, if you are still taking votes on that:We're considering adding this using a /literal decorator at the end of a quoted or swim string. For example,
"Not $escaped"/literal
.If you have any interest in this, please submit a ticket with how you would plan to use this, and how it would be helpful; this is currently tracked as IEE-20, and we will link your ticket and seek to prioritize this.
-
PSExec
(i.e.Execute-Powershell
) can capture variables, but not output streams.Understood with regards to output stream.
However, no joy with this (i.e. capturing variables) either...
set $In = 12345; set $Out = '<unset>'; Execute-PowerShell ( Text: >-|> Write-Verbose "Got some input: $In here..."; $Out = "This is nice"; Write-Verbose "inside the script, we have: $Out"; >-|>, Verbose: true ); Log-Information Outside the script, we have $Out;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing Out... DEBUG: Importing In... DEBUG: Got some input: 12345 here... DEBUG: inside the script, we have: This is nice INFO : Outside the script, we have <unset>
So something like this:
set $hello = world; $PSExec >> $hello = 'dears'; >>; Log-Information Hello $hello;
Calling
$PSExec
like this throws Unexpected token $. I suspect you meant to writePSExec
(the operation) or$PSEval
(the variable function).However, substituting
PSExec >>
does not capture the output variable.set $hello = world; PSExec >> $hello = 'dears'; >>; Log-Information 1: Hello $hello;
DEBUG Using Windows PowerShell 5.1... DEBUG Importing hello... INFO 1: Hello world
Substituting
set $x = $PSEval(>>...>>)
does not even execute: The term '>>' is not recognized as the name of a cmdlet, function, script file, or operable programset $hello = world; set $x = $PSEval(>> $hello = 'dears'; >>); Log-Information 2: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... ERROR: The term '>>' 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. ERROR: The term 'world' 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. ERROR: The term '>>' 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. ERROR: PSEVal: PowerShell script failed with an error (see previous log messages).
set $hello = world; set $x = $PSEval(>> `$hello = 'dears'; >>); Log-Information 3: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing hello... ERROR: The term '>>' 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. ERROR: The term '>>' 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. ERROR: PSEVal: PowerShell script failed with an error (see previous log messages).
set $hello = world; set $x = $PSEval(>>`$hello = 'dears';>>); Log-Information 4: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing hello... ERROR: The term '>>' 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. ERROR: The term '>>' 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. ERROR: PSEVal: PowerShell script failed with an error (see previous log messages).
Substituting
set $x = $PSEval("...")
does not capture the output variable:set $hello = world; set $x = $PSEval(" $hello = 'dears'; "); Log-Information 5: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing hello... INFO : 5: Hello world
set $hello = world; set $x = $PSEval(" `$hello = 'dears'; "); Log-Information 6: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing hello... INFO : 6: Hello world
I think this might actually make my point for me. If you can get this wrong instinctively, what hope do I have (or my less code-savvy colleagues)?
-
@jimbobmcgee thanks; we'll definitely investigate this later, but it will likely not be for a few months until we can do some "heads down" time with this stuff
Honestly I don't remember how any of this works, so I could be wrong and you need to do something else. It's clearly not something we document.
Our primary use case is more like this, uploading basic scripts:
https://docs.inedo.com/docs/otter/scripting-in-otter/otter-scripting-powershell