How do I implement output variables in powershell?
I realize that "output variables" is probably the wrong terminology, so my google search failed me. I'm guessing it revolves around an explicitly defined variable scope, but I've tried reading the about_Scopes doc and it just didn't click on me.
Basically, I am trying to implement the equivalent of the argument -SessionVariable
from Invoke-RestMethod in my own module function.In other words, I need a string parameter that turns into a variable in the call scope.
function MyTest
{
param([string]$outvar)
# caller scoped variable magic goes here
set-variable -Name $outvar -value "hello world"
# normal function output to pipeline here
Write-Output "my return value"
}
# calling the function initially outputs "my return value"
MyTest -outvar myvar
# referencing the variable outputs "hello world"
$myvar
For bonus points, how does things change (if at all) if I wrap an existing function that has its own output variable and I want to effectively pass the name of the output variable?
function MyWrapper
{
param([string]$SessionVariable)
Invoke-RestMethod -Uri "http://myhost" -SessionVariable $SessionVariable
# caller scoped variable magic goes here
set-variable -Name $SessionVariable -value $SessionVariable
}
# calling the wrapper outputs the normal results from Invoke-RestMethod
MyWrapper -SessionVariable myvar
# calling the variable outputs the WebRequestSession object from the inner Invoke-RestMethod call
$myvar
PS If it matters I am trying to keep the module Powershell v3 + compatible.
source to share
@Mathias R. Jessen offers a solution that works well if not defined in a module. But not if you put it in a module. So, I provide another solution that works when it is placed in a module.
In an extended function (using an attribute [CmdletBinding()]
) you can use $PSCmdlet.SessionState
to refer to the SessionState
caller. So you can use $PSCmdlet.SessionState.PSVariable.Set('Name' ,'Value')
to set variables in caller SessionState
.
Note. This solution will not work if it is not defined in the module or if it is called from the same module where it is defined.
New-Module {
function MyTest1 {
[CmdletBinding()]
param([string]$outvar)
$PSCmdlet.SessionState.PSVariable.Set($outvar, 'Some value')
}
function MyTest2 {
[CmdletBinding()]
param([string]$outvar)
# -Scope 1 not work, because it executed from module SessionState,
# and thus have separate hierarchy of scopes
Set-Variable -Name $outvar -Value 'Some other value' -Scope 1
}
function MyTest3 {
[CmdletBinding()]
param([string]$outvar)
# -Scope 2 will refer to global scope, not the caller scope,
# so variable with same name in caller scope will hide global variable
Set-Variable -Name $outvar -Value 'Some other value' -Scope 2
}
} | Out-Null
& {
$global:a = 'Global value'
MyTest1 a
$a
$global:a
''
MyTest2 a
$a
$global:a
''
MyTest3 a
$a
$global:a
}
source to share
There is no need to try to implement this yourself, -OutVariable
the General option is .
Add CmdletBinding
an attribute to your block param
to get them for free:
function MyTest {
[CmdletBinding()]
param()
return "hello world"
}
MyTest -OutVariable testvar
$testvar
now contains the string value "hello world"
For your second example, where you need to set a value in the call scope in addition to the output pipeline, use the -Scope
c parameter Set-Variable
:
function MyTest {
[CmdletBinding()]
param([string]$AnotherVariable)
if([string]::IsNullOrWhiteSpace($AnotherVariable)){
Set-Variable -Name $AnotherVariable -Value 'more data' -Scope 1
}
return "hello world"
}
MyTest -AnotherVariable myvar
$myvar
in the call area now contains the string value "more data". The value 1
passed to the parameter Scope
means "1 level up"
source to share