Powershell gurus, please clarify variable scope in function

15,712

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.

Share:
15,712
Emiliano Poggi
Author by

Emiliano Poggi

profile for empo on Stack Exchange, a network of free, community-driven Q&A sites http://stackexchange.com/users/flair/92635.png?theme=dark

Updated on June 12, 2022

Comments

  • Emiliano Poggi
    Emiliano Poggi almost 2 years

    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?

  • Emiliano Poggi
    Emiliano Poggi about 13 years
    ok, that's make sense! Didn't know functions can be dot-source :). Then the about_scripts at microsoft technet is not correct I think. Read the section SCRIPT SCOPE AND DOT SOURCING. Is that correct?
  • Keith Hill
    Keith Hill about 13 years
    Execute man about_scopes from the PowerShell prompt. That would be a good help topic to read.
  • Jaykul
    Jaykul about 13 years
    And using Set-StrictMode -v latest can help you avoid accidentally referring to variables from external scope ...