by arnoldus at 2012-08-26 09:44:43
I don’t understand how you control what a function is allowed to do with the variables.by poshoholic at 2012-08-26 19:01:06
In my functions (that are on the same script file as the main script), changes to variables inside a function only persist until the function ends, then the vars return to their old values.
How can I modify this behaviour?
I don’t use functions as a black box, I use them as a subroutine, a fast way to do something repetitive. But changes made must then persist.
If you want to persist variable changes from a child scope (e.g. from inside of a function or a script block) into a parent scope, then you need to specifically identify the scope.by arnoldus at 2012-08-27 01:41:28
For example, look at this script which demonstrates the difference between assigning a new local variable inside a function (even if it has the same name as a variable outside of a function) and deliberately assigning a value to a variable in a parent scope:
[script=powershell]$TestVariable = 'Original value'
function Test-Persist {
param(
[switch]$UseScope
)
if ($UseScope) {
# This sets the value of $TestVariable in the parent (1) scope. 0 = current scope, 1 = parent, 2 = grandparent, etc.
Set-Variable -Name TestVariable -Scope 1 -Value 'Changed in Test-Persist'
} else {
# This creates a new $TestVariable variable in the child scope that has nothing to do with the $TestVariable variable in the parent scope.
$TestVariable = 'Changed in Test-Persist'
}
}
"Value before first test: $TestVariable"
Test-Persist
"Value after simple assignment from inside Test-Persist: $TestVariable"
Test-Persist -UseScope
"Value after deliberate assignment with scope from inside Test-Persist: $TestVariable"[/script]
I think you should be able to extract what you need to do from that example. If not, please let me know.
That does not really help AFAIS, you can not do arithmetic with the previous value, only set a value (yeah, you could import all variables as params and do changes on those and then set them before exitting, but then it’s just simpler doing everything in the main body).by JeffH at 2012-08-27 04:20:23
I guess I need to pull the functions back into the main body of code.
EDIT: dot source!
Dot space function: ". myfunction", and I got what I need.
Scope is a topic that trips up many people. If you haven’t already, read the about topic on Scope.by DexterPOSH at 2012-08-27 07:53:14
Hi Arnoldus,by mjolinor at 2012-08-30 16:07:35
I think Kirk explained it very well,
Child is able to see the variables in Parent Scope but to modify it you need to explicitly tell that you are modifying variable of the Parent Scope.
That’s what line 9 in the code is doing.Set-Variable -Name TestVariable -Scope 1 -Value ‘Changed in Test-Persist’
Variables created within a scope only persist as long as the scope. Operations that only update a property or element of an existing object do not create a new object. Many times the kind of scope problems you describe can be solved quite simply by switching to using a hash table to hold your values, rather than discrete variables:by DonJ at 2012-08-30 16:18:52
$MyVar = @{
Counter = 0
Sum = 0
Names = @()
LastRun = $nul
}
function update {
$MyVar.Counter ++
$MyVar.Sum += 10
$MyVar.Names += ‘Don’,‘Jeff’,‘Kirk’
$MyVar.LastRun = (get-date)
}
$myVar
‘*’*10
update
$myVar
I’d strongly suggest reading the about_scope help topic in the shell.
It isn’t a question of the "variables returning to their original values." When you assign a variable inside a function, the shell creates a new variable and assigns the value. If that variable name was already in use outside the function, then the in-function variable essentially "masks" the out-of-function version. When the function ends, everything created inside the function goes away. It’s a bit more subtle in terms of behavior than you might realize.
As a general practice, functions should avoid modifying variables outside themselves. There are some reasons to do so, but in general a function should pass values out as via the pipeline. In other words, rather than this:
$x = 5
function mine {
set-variable -name x -scope 1 -value ($x + 1)
}
mine
You should do this:
$x = 5
function mine {
param($in_x)
$in_x = $in_x + 1
write-output $x_in
}
$result = mine $x
In other words, all values the function needs to work with should be explicitly passed in via parameters, not assumed from the parent scope. Anything the calling scope needs from the function should be output by the function, either using the Return keyword or the Write-Output command. Again, there are times when you have to make an exception to that general rule - but it’s a good rule to try and follow as it makes the functions better-encapsulated and more standalone. It’s also, frankly, less confusing.