62

I need to set a global variable from a function and am not quite sure how to do it.

# Set variables
$global:var1
$global:var2
$global:var3

function foo ($a, $b, $c)
{
    # Add $a and $b and set the requested global variable to equal to it
    $c = $a + $b
}

Call the function:

foo 1 2 $global:var3

End result:

$global:var3 is set to 3

Or if I called the function like this:

foo 1 2 $global:var2

End result:

$global:var2 is set to 3

I hope this example makes sense. The third variable passed to the function is the name of the variable it is to set.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Radagast
  • 1,026
  • 6
  • 15
  • 21

7 Answers7

99

You can use the Set-Variable cmdlet. Passing $global:var3 sends the value of $var3, which is not what you want. You want to send the name.

$global:var1 = $null

function foo ($a, $b, $varName)
{
   Set-Variable -Name $varName -Value ($a + $b) -Scope Global
}

foo 1 2 var1

This is not very good programming practice, though. Below would be much more straightforward, and less likely to introduce bugs later:

$global:var1 = $null

function ComputeNewValue ($a, $b)
{
   $a + $b
}

$global:var1 = ComputeNewValue 1 2
latkin
  • 16,402
  • 1
  • 47
  • 62
  • Could I use the Set-Variable cmdlet in the user profile file? So whenever the pc starts it has that global variable? – John Demetriou Apr 04 '16 at 11:11
  • When I try executing the profile.ps1 using powershell_ise and it executes normally. When I try to execute the profile using & $profile I have a an error parameterBingingValidationException – John Demetriou Apr 04 '16 at 11:15
49

As simple as:

$A="1"
function changeA2 () { $global:A="0"}
changeA2
$A
Kirt Carson
  • 756
  • 6
  • 3
  • 2
    @Schiavini -- I think it does work. If it's throwing you off, remember that the first `$A` is, if entered directly on the cmd line, global, and *should* be overwritten when tried in global scope. Try adding the line `$global:A` afterwards if you want. Here's a one-liner via semi-colons that takes in parameters and does the same thing: `$A = -777;function changeA2 ($p1, $p2) { $global:A=$p1+$p2}; changeA2 4 5; $A; "explicit global: " + $global:A;` Now is it horrible spaghetti code? That's a separate discussion. ;^) I'm biased towards latkin's second solution. – ruffin Oct 16 '14 at 17:34
25

I ran across this question while troubleshooting my own code.

So this does NOT work...

$myLogText = ""
function AddLog ($Message)
{
    $myLogText += ($Message)
}
AddLog ("Hello")
Write-Host $myLogText

This APPEARS to work, but only in the PowerShell ISE:

$myLogText = ""
function AddLog ($Message)
{
    $global:myLogText += ($Message)
}
AddLog ("Hello")
Write-Host $myLogText

This is actually what works in both ISE and command line:

$global:myLogText = ""
function AddLog ($Message)
{
    $global:myLogText += ($Message)
}
AddLog ("Hello")
Write-Host $global:myLogText
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
da_jokker
  • 954
  • 2
  • 12
  • 16
  • 1
    I couldn't make even the 3rd example to work - still not assigning via command line (works on VS and ISE) – Eitan Feb 20 '17 at 10:35
  • I am seriously so annoyed by this powershell weirdness! Seems to me that declaring a variable in a top level (not in function) should make it global and would make sense to me! And yes it does but only in ISE! So you write code and then it frikin breaks in the console, because you didn't use $global: when declaring a variable. How can be variables declared in the main script body not global? – McVitas May 31 '20 at 18:14
  • which is even more weird is, that you DON'T need to use $global: in case of custom objects with properties. They are accessible from anywhere without $global: – McVitas May 31 '20 at 18:46
19

You'll have to pass your arguments as reference types.

#First create the variables (note you have to set them to something)
$global:var1 = $null
$global:var2 = $null
$global:var3 = $null

#The type of the reference argument should be of type [REF]
function foo ($a, $b, [REF]$c)
{
    # add $a and $b and set the requested global variable to equal to it
    # Note how you modify the value.
    $c.Value = $a + $b
}

#You can then call it like this:
foo 1 2 [REF]$global:var3
zdan
  • 28,667
  • 7
  • 60
  • 71
  • 2
    I think that the closest you can come to a true return value in PoSH is to use a _local variable_ to pass the value and never to use `return` as it may be 'corrupted' by any manner of output situations `function CheckRestart([REF]$retval) { # Some logic $retval.Value = $true } [bool]$restart = $false CheckRestart( [REF]$restart) if ( $restart ) { Restart-Computer -Force }` – RobG May 14 '15 at 02:37
3

The first suggestion in latkin's answer seems good, although I would suggest the less long-winded way below.

PS c:\temp> $global:test="one"

PS c:\temp> $test
one

PS c:\temp> function changet() {$global:test="two"}

PS c:\temp> changet

PS c:\temp> $test
two

His second suggestion however about being bad programming practice, is fair enough in a simple computation like this one, but what if you want to return a more complicated output from your variable? For example, what if you wanted the function to return an array or an object? That's where, for me, PowerShell functions seem to fail woefully. Meaning you have no choice other than to pass it back from the function using a global variable. For example:

PS c:\temp> function changet([byte]$a,[byte]$b,[byte]$c) {$global:test=@(($a+$b),$c,($a+$c))}

PS c:\temp> changet 1 2 3

PS c:\temp> $test
3
3
4

PS C:\nb> $test[2]
4

I know this might feel like a bit of a digression, but I feel in order to answer the original question we need to establish whether global variables are bad programming practice and whether, in more complex functions, there is a better way. (If there is one I'd be interested to here it.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
AutoMattTick
  • 453
  • 4
  • 6
1

@zdan. Good answer. I'd improve it like this...

I think that the closest you can come to a true return value in PowerShell is to use a local variable to pass the value and never to use return as it may be 'corrupted' by any manner of output situations

function CheckRestart([REF]$retval)
{
    # Some logic
    $retval.Value = $true
}
[bool]$restart = $false
CheckRestart( [REF]$restart)
if ( $restart )
{
    Restart-Computer -Force
}

The $restart variable is used either side of the call to the function CheckRestart making clear the scope of the variable. The return value can by convention be either the first or last parameter declared. I prefer last.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RobG
  • 744
  • 6
  • 16
-1

Set the variable as a global variable outside of the function and then set the value inside of the function.

Bbb
  • 517
  • 6
  • 27