2

I want to know why some of my variables are working although they are not declared as global For an example:

Function B {
    #Some code..

    if ($var1 -eq 'true') {
        Do something 
    }
}

Function A {
    $var1 = 'false'
    $var2 = 'false'

    #Some code.. 
    if ($a -eq "1") {
        $var1 = 'true'
    }
    #Call function B
    Function B
}

There are more functions in my code but the variables we not be exposed unless I will do this: So I am wondering why in the case above its working. I have many cases that its not working without declaring the variable as global

Set-Variable -Name "varname" -Value $varname -Scope global

How function B supposed to get the changes of $var1 when it changed in function A?

Bandit
  • 399
  • 3
  • 10
  • you might want to expand after the "for example" because looking at your code none of your functions are returning anything. please don't expect us to debug your unclear code, be more specific – Santiago Squarzon Dec 23 '22 at 22:44
  • are you sure that those variables already do not exist as global? and you use them in global scope. check variables with Get-Variable foo -Scope Global – Konstantin Purtov Dec 23 '22 at 22:46
  • None of my functions are not returning values. I am using with global variables only, The code is working and I don't need to debug. just want to know why its working while function is calling to other function – Bandit Dec 23 '22 at 22:48
  • @Konstantin Purtov Sure in 100% – Bandit Dec 23 '22 at 22:49
  • What exactly is supposed to happen with your pseudo code? I'm not clear on what you mean – Santiago Squarzon Dec 23 '22 at 22:54
  • @Santiago Squarzon I fixed the example After calling to function B the $var1 = true and the 'Do something' is done without set to global – Bandit Dec 23 '22 at 23:04
  • functions can see the variables in the caller scope, since `$var1` is defined in the scope of `A` and the caller of `B` is also `A` then `B` can see those variables. They don't need to be defined as `$global:` or even `$script:` for that – Santiago Squarzon Dec 23 '22 at 23:11
  • PowerShell uses [dynamic scoping](http://ig2600.blogspot.com/2010/01/powershell-is-dynamically-scoped-and.html), while most other languages use _lexical_ scoping. Dynamic scoping lets code in child scopes, such as functions, _read_ the value of variables from all parent scopes. Once child scope trys to modify a variable, it becomes a local variable though. Quite confusing? I strongly suggest to read the linked article. – zett42 Dec 23 '22 at 23:19

1 Answers1

5

As stated in about_Scopes:

  • An item is visible in the scope in which it was created and in any child scopes, unless you explicitly make it private.

$var1 is a variable defined in the scope of A and B is called from the same scope, hence, $var1 is also available to B.

Simplifying your example:

function B {
    $var1
}

function A {
    $var1 = 'hello'
    B
}

A # => 'hello'

However, as stated in the documentation, if you would like to have $var1 only available to A you can make it $private::

function B {
    $var1
}

function A {
    $private:var1 = 'hello'
    B
}

A # => NULL
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • Thank you for that. I have one more question In this code how function B supposed to get the changes of $var1 when it changed in function A? – Bandit Dec 24 '22 at 07:57
  • I read this but don't understand that: "unless you explicitly specify the scope when you create the items" ------------------------------------------------------------------------------------------- Unless you explicitly make the items private, the items in the parent scope are available to the child scope. However, items that you create and change in the child scope do not affect the parent scope, unless you explicitly specify the scope when you create the items. – Bandit Dec 24 '22 at 08:41
  • @Bandit, it means that if you change the variable in the child scope (try to write to it, rather than just read it), it automatically creates a new variable (with the name) in that (child) scope. From then on, the new created variable in the child scope takes priority over the one in the parent scope. – iRon Dec 24 '22 at 08:47
  • Thanks @iRon But how the parent can get the new value of the child var? – Bandit Dec 24 '22 at 08:50
  • You might overwrite the variable in the parent scope as follows: `([ref]$var1).Value = 'World'` – iRon Dec 24 '22 at 08:53
  • 1
    @iRon you mean that I need to write this ([ref]$var1).Value = 'World' in the child scope in order to change the parent var? – Bandit Dec 24 '22 at 09:03
  • 1
    Yes, to be more precise: to change the variable of the closest parent that created it (this might be a grandparent in case there is nothing created by the direct parent). – iRon Dec 24 '22 at 09:19
  • The ([ref]$var1).Value = 'World' is working for me – Bandit Dec 24 '22 at 09:36
  • @iRon Its working only for value but if I have a list for an example it won't work. there is a solution for array/list? – Bandit Dec 24 '22 at 09:50
  • @Bandit, properties of an object (array/hashtable `$vars = @{ var1 = 'false', var2 = 'false' }`, see: [how to programmatically iterate through variable names](https://stackoverflow.com/a/65250334/1701026) ) are by reference anyhow. Please open a new question with a [mcve] of what exactly you trying to accomplish. – iRon Dec 24 '22 at 10:04
  • https://stackoverflow.com/questions/74906998/powershell-how-to-pass-an-array-by-ref – Bandit Dec 24 '22 at 10:34