Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Powershell gurus, please clarify variable scope in function

I read that variables inside the scope of a function are accessible in the current scope when the function script is dot-sourced.

Is it true? It's very strange, and unusual I think...

Can me clarify the reasons of this. Example, my-f sets $my-f-var to some integer, let's say 2:

PS1> . .\my-f-script.ps1
PS1> my-f
PS1> $my-f-var
2

I would expect that $my-f-var is not accessible because inside the function! Is there a way to make variables private, or a way to call the function without dot-sourcing its script?

like image 424
Emiliano Poggi Avatar asked Apr 02 '11 06:04

Emiliano Poggi


1 Answers

Variables within the function are local to that scope unless you dot the invocation of the function name. When you dot source a script, the top-level script variables are effectively imported into the current scope along with the function definitions e.g.:

PS> '$scriptvar = 2; function my-f { ${my-f-var} = 2 }' > my-f-script.ps1
PS> Remove-Variable scriptvar, my-f-var
Remove-Variable : Cannot find a variable with name 'scriptvar'.
Remove-Variable : Cannot find a variable with name 'my-f-var'.
PS> . .\my-f-script.ps1
PS> $scriptvar
2
PS> ${my-f-var}
PS>

Note that ${my-f-var} isn't defined in the local scope. However, if i 'dot' the invocation of the function then its contents are run in the current scope e.g.:

PS> . my-f
PS> ${my-f-var}
2

In this case, the variable set in the function is set in the current (invoking) scope because of the 'dot' used to invoke it.

A couple of more points: you can access variables at various scopes using either Get-Variable -scope or the more convenient (if less flexible) global, script, local and private modifiers. When you access a variable inside a function where the variable is defined in a higher scope, you can read it just fine. But when you set it, PowerShell essentially does a "copy-on-write" of the variable - creating a new copy scoped from that function downward (ie to the other functions it calls). If you really want to modify a higher scoped variable you can use $global:Foo or $script:Foo to modify at those scopes.

The local scope comes in handy if you want to avoid inadvertently using a variable defined outside of your function. Another MVP brought this up at the last MVP summit and it seems like one of those "best practice" tips. Here's the scenario:

PS> $foo = 'bad'
PS> function testscope { $fooo = 'good'; "The value of `$fooo is $foo" }
PS> testscope
The value of $fooo is bad

Note that in this case the use of $foo inside the function is a typo but coincidentally there is a variable by that typo'd name. This was not the intent of the function and it is not working correctly although that is hard to see in this case. Below, we can use the local specifier to make sure the function only looks within the local scope for the variable. It doesn't find it because of the typo but at least the error is a bit easier to spot now.

PS> function testscope { $fooo = 'good'; "The value of `$fooo is $local:foo" }
PS> testscope
The value of $fooo is

The private scope is useful when you don't want certain variables to be visible to the other functions that your function calls.

like image 105
Keith Hill Avatar answered Oct 11 '22 01:10

Keith Hill