I realize "output variables" is probably the wrong terminology here which is why my google searching has failed me. I'm guessing it revolves around explicitly setting variable scope, but I've tried reading the about_Scopes doc and it's just not clicking for me.
Essentially what I'm trying to do is implement the equivalent of the -SessionVariable
argument from Invoke-RestMethod in my own module's function. In other words, I need a string parameter that turns into a variable in the caller's 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 do things change (if at all) if I'm wrapping an existing function that has its own output variable and I want to effectively pass through the output variable name?
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
P.S. If it matters, I'm trying to keep the module compatible with Powershell v3+.
There's no need to attempt to implement this yourself, -OutVariable
is a Common Parameter.
Add a CmdletBinding
attribute to your param
block to get these for free:
function MyTest {
[CmdletBinding()]
param()
return "hello world"
}
MyTest -OutVariable testvar
$testvar
now holds the string value "hello world"
For your second example, where you need to set a value in the caller scope in addition to pipeline output, use the -Scope
option with 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 caller scope now contains the string value "more data". The 1
value passed to the Scope
parameter means "1 level up"
@Mathias R. Jessen propose solution, which work well, when not defined in module. But not, if you put it in module. So, I provide another solution, which works, when put in module.
In advanced function (one which use [CmdletBinding()]
attribute) you can use $PSCmdlet.SessionState
to refer to SessionState
of a caller. Thus, you can use $PSCmdlet.SessionState.PSVariable.Set('Name' ,'Value')
to set variables in caller's SessionState
.
Note: this solution will not work if not defined in module or if called from the same module, where it 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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With