Skip to content

Commit fbc493b

Browse files
Fixes #10797 - Copy changes to all versions (#10831)
* Clarify how scopes work and their relations to runspaces --------- Co-authored-by: James Truher [MSFT] <jimtru@microsoft.com>
1 parent 97cfaa6 commit fbc493b

File tree

5 files changed

+875
-570
lines changed

5 files changed

+875
-570
lines changed

reference/5.1/Microsoft.PowerShell.Core/About/about_Scopes.md

Lines changed: 175 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: Explains the concept of scope in PowerShell and shows how to set and change the scope of elements.
33
Locale: en-US
4-
ms.date: 03/31/2023
4+
ms.date: 01/26/2024
55
online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-5.1&WT.mc_id=ps-gethelp
66
schema: 2.0.0
77
title: about Scopes
@@ -16,40 +16,35 @@ the scope of elements.
1616

1717
PowerShell protects access to variables, aliases, functions, and PowerShell
1818
drives (PSDrives) by limiting where they can be read and changed. PowerShell
19-
uses scope rules to ensure that you don't inadvertently change an item that
20-
shouldn't be changed.
19+
uses scope rules to ensure that you don't make unintentional changes to items
20+
in other scopes.
21+
22+
## Scope rules
23+
24+
When you start PowerShell, the host (`pwsh.exe`) creates a PowerShell runspace.
25+
Host processes can have multiple runspaces. Each runspace has its own session
26+
state and scope containers. Session state and scopes can't be accessed across
27+
runspace instances.
2128

2229
The following are the basic rules of scope:
2330

2431
- Scopes may nest. An outer scope is referred to as a parent scope. Any nested
2532
scopes are child scopes of that parent.
26-
27-
- An item is visible in the scope that it was created in and in any child
28-
scopes, unless you explicitly make it private.
33+
- An item is visible in the scope that it was created and in any child scopes,
34+
unless you explicitly make it private.
2935
- You can declare variables, aliases, functions, and PowerShell drives for a
3036
scope outside of the current scope.
3137
- An item that you created within a scope can be changed only in the scope in
3238
which it was created, unless you explicitly specify a different scope.
33-
34-
If you create an item in a scope, and the item shares its name with an item in
35-
a different scope, the original item might be hidden by the new item, but it
36-
isn't overridden or changed.
37-
38-
## PowerShell scopes
39-
40-
PowerShell supports the following scopes:
41-
42-
- **Global**: The scope that's in effect when PowerShell starts or when you
43-
create a new session or runspace. Variables and functions that are present
44-
when PowerShell starts have been created in the global scope, such as
45-
automatic variables and preference variables. The variables, aliases, and
46-
functions in your PowerShell profiles are also created in the global scope.
47-
The global scope is the root parent scope in a session.
48-
- **Local**: The current scope. The local scope can be the global scope or any
49-
other scope.
50-
- **Script**: The scope that's created while a script file runs. Only the
51-
commands in the script run in the script scope. To the commands in a script,
52-
the script scope is the local scope.
39+
- When code running in a runspace references an item, PowerShell searches the
40+
scope hierarchy, starting with the current scope and proceeding through each
41+
parent scope. If the item isn't found, a new item is created in the current
42+
scope. If it finds a match, the value of the item is retrieved from the scope
43+
where is was found. If you change value, the item copied to the current scope
44+
so that the change only affects the current scope.
45+
- If you explicitly create an item that shares its name with an item in a
46+
different scope, the original item might be hidden by the new item, but it
47+
isn't overridden or changed.
5348

5449
## Parent and child scopes
5550

@@ -58,28 +53,21 @@ scope is the parent scope. The called script or function is the child scope.
5853
The functions or scripts you call may call other functions, creating a
5954
hierarchy of child scopes whose root scope is the global scope.
6055

61-
Unless you explicitly make the items private, the items in the parent scope
62-
are available to the child scope. However, items that you create and change in
63-
the child scope don't affect the parent scope, unless you explicitly specify
64-
the scope when you create the items.
65-
6656
> [!NOTE]
6757
> Functions from a module don't run in a child scope of the calling scope.
68-
> Modules have their own session state that's linked to the global scope.
69-
> All module code runs in a module-specific hierarchy of scopes that has its
70-
> own root scope.
71-
72-
## Inheritance
58+
> Modules have their own session state that's linked to the scope in which the
59+
> module was imported. All module code runs in a module-specific hierarchy of
60+
> scopes that has its own root scope. For more information, see the
61+
> [Modules][02] section of this article.
7362
74-
A child scope doesn't inherit the variables, aliases, and functions from the
75-
parent scope. Unless an item is private, the child scope can view the items in
76-
the parent scope. And, it can change the items by explicitly specifying the
77-
parent scope, but the items aren't part of the child scope.
63+
When a child scope is created, it includes all the aliases and variables that
64+
have the **AllScope** option, and some automatic variables. This option is
65+
discussed later in this article.
7866

79-
However, a child scope is created with a set of items. Typically, it includes
80-
all the aliases that have the **AllScope** option. This option is discussed
81-
later in this article. It includes all the variables that have the **AllScope**
82-
option, plus some automatic variables.
67+
Unless you explicitly make the items private, the items in the parent scope are
68+
available to the child scope. Items that you create or change in a child scope
69+
don't affect the parent scope, unless you explicitly specify the scope when you
70+
create the items.
8371

8472
To find the items in a particular scope, use the Scope parameter of
8573
`Get-Variable` or `Get-Alias`.
@@ -96,6 +84,36 @@ To get all the variables in the global scope, type:
9684
Get-Variable -Scope global
9785
```
9886

87+
When a reference is made to a variable, alias, or function, PowerShell searches
88+
the current scope. If the item isn't found, the parent scope is searched. This
89+
search is repeated all they way up to the global scope. If a variable is
90+
private in a parent scope, the search through continues through the scope
91+
chain. [Example 4][01] shows the the effect of a private variable in a scope
92+
search.
93+
94+
## PowerShell scopes names
95+
96+
PowerShell defines names for some scopes to allow easier access to that scope.
97+
PowerShell defines the following named scopes:
98+
99+
- **Global**: The scope that's in effect when PowerShell starts or when you
100+
create a new session or runspace. Variables and functions that are present
101+
when PowerShell starts, such as automatic variables and preference variables,
102+
are created in the global scope. The variables, aliases, and functions in
103+
your PowerShell profiles are also created in the global scope. The global
104+
scope is the root parent scope in a runspace.
105+
- **Local**: The current scope. The local scope can be the global scope or any
106+
other scope.
107+
- **Script**: The scope that's created while a script file runs. The commands
108+
in the script run in the script scope. For the commands in a script, the
109+
script scope is the local scope.
110+
111+
For cmdlets that support scopes, scopes can be referred to by a number that
112+
describes the relative position of one scope to another. Scope 0 denotes the
113+
current (local) scope, scope 1 is the current scope's parent, scope 2 is the
114+
current scope's grandparent. This pattern continues until you reach the root
115+
scope.
116+
99117
## Scope modifiers
100118

101119
A variable, alias, or function name can include any one of the following
@@ -108,8 +126,8 @@ optional scope modifiers:
108126
current scope.
109127

110128
> [!NOTE]
111-
> `private` isn't a scope. It's an [option][02] that changes the visibility
112-
> of an item outside of the scope where the item is defined.
129+
> `private:` isn't a scope. It's an [option][03] that changes the
130+
> accessibility of an item outside of the scope in which it's defined.
113131
114132
- `script:` - Specifies that the name exists in the **Script** scope.
115133
**Script** scope is the nearest ancestor script file's scope or **Global** if
@@ -246,16 +264,16 @@ Depending on the context, embedded variable values are either independent
246264
copies of the data in the caller's scope or references to it. In remote and
247265
out-of-process sessions, they're always independent copies.
248266

249-
For more information, see [about_Remote_Variables][06].
267+
For more information, see [about_Remote_Variables][07].
250268

251269
In thread sessions, they're passed by reference. This means it's possible to
252270
modify child scope variables in a different thread. To safely modify variables
253271
requires thread synchronization.
254272

255273
For more information see:
256274

257-
- [Start-ThreadJob][10]
258-
- [ForEach-Object][09]
275+
- [Start-ThreadJob][11]
276+
- [ForEach-Object][10]
259277

260278
### Serialization of variable values
261279

@@ -367,7 +385,7 @@ Using the call operator is no different than running the script by name.
367385
& c:\scripts\sample.ps1
368386
```
369387

370-
You can read more about the call operator in [about_Operators][05].
388+
You can read more about the call operator in [about_Operators][06].
371389

372390
To run the `Sample.ps1` script in the local scope type a dot and a space (`. `)
373391
before the path to the script:
@@ -405,28 +423,15 @@ example, you can run a script to create a child scope in a session.
405423

406424
You can use a PowerShell module to share and deliver PowerShell tools. A module
407425
is a unit that can contain cmdlets, scripts, functions, variables, aliases, and
408-
other useful items. Unless explicitly defined, the items in a module aren't
409-
accessible outside the module. Therefore, you can add the module to your
410-
session and use the public items without worrying that the other items might
411-
override the cmdlets, scripts, functions, and other items in your session.
412-
413-
By default, modules are loaded into the top-level of the current _session
414-
state_ not the current _scope_. The current session state could be a module
415-
session state or the global session state. Adding a module to a session does
416-
not change the scope. If you are in the global scope, then modules are loaded
417-
into the global session state. Any exports are placed into the global tables.
418-
If you load module2 from _within_ module1, module2 is loaded into the session
419-
state of module1 not the global session state. Any exports from module2 are
420-
placed at the top of the module1 session state. If you use
421-
`Import-Module -Scope local`, then the exports are placed into the current
422-
scope object rather than at the top level. If you are _in a module_ and use
423-
`Import-Module -Scope global` (or `Import-Module -Global`) to load another
424-
module, that module and its exports are loaded into the global session state
425-
instead of the module's local session state. This feature was designed for
426-
writing module that manipulate modules. The **WindowsCompatibility** module
427-
does this to import proxy modules into the global session state.
428-
429-
Within the session state, modules have their own scope. Consider the following
426+
other useful items. Unless explicitly exported (using `Export-ModuleMember` or
427+
the module manifest), the items in a module aren't accessible outside the
428+
module. Therefore, you can add the module to your session and use the public
429+
items without worrying that the other items might override the cmdlets,
430+
scripts, functions, and other items in your session.
431+
432+
By default, modules are loaded into the root-level (global) scope of the
433+
runspace. Importing a module doesn't change the scope.
434+
Within the session, modules have their own scope. Consider the following
430435
module `C:\temp\mod1.psm1`:
431436

432437
```powershell
@@ -454,6 +459,23 @@ $a = Hello
454459
$global:a = Goodbye
455460
```
456461

462+
Modules create parallel scope containers linked to the scope in which they were
463+
imported. Items exported by the module are available starting at the
464+
scope-level in which they are imported. Items not exported from the module are
465+
only available within the module's scope container. Functions in the module can
466+
access items in the scope in which they were imported as well as items in the
467+
module's scope container.
468+
469+
If you load **Module2** from _within_ **Module1**, **Module2** is loaded into
470+
the scope container of Module1. Any exports from **Module2** are placed in the
471+
current module scope of **Module1**. If you use `Import-Module -Scope local`,
472+
then the exports are placed into the current scope object rather than at the
473+
top level. If you are _in a module_ and load another module using
474+
`Import-Module -Scope global` (or `Import-Module -Global`), that module and its
475+
exports are loaded into the global scope instead of the module's local scope.
476+
The **WindowsCompatibility** feature does this to import proxy modules into the
477+
global session state.
478+
457479
### Nested prompts
458480

459481
Nested prompts don't have their own scope. When you enter a nested prompt, the
@@ -612,44 +634,81 @@ Local
612634

613635
### Example 4: Creating a private variable
614636

615-
A private variable is a variable that has an **Option** property that has a
616-
value of `Private`. `Private` variables are inherited by the child scope, but
617-
they can only be viewed or changed in the scope in which they were created.
637+
A variable can be made private by using the `private:` scope modifier or by
638+
creating the variable with the **Option** property set to `Private`. Private
639+
variables can only be viewed or changed in the scope in which they were
640+
created.
618641

619-
The following command creates a private variable called `$ptest` in the local
620-
scope.
642+
In this example, the `ScopeExample.ps1` script creates five functions. The
643+
first function calls the next function, which creates a child scope. One of the
644+
functions has a private variable that can only be seen in the scope in which it
645+
was created.
621646

622647
```powershell
623-
New-Variable -Name ptest -Value 1 -Option Private
624-
```
625-
626-
You can display and change the value of `$ptest` in the local scope.
627-
628-
```
629-
PS> $ptest
630-
1
648+
PS> Get-Content ScopeExample.ps1
649+
# Start of ScopeExample.ps1
650+
function funcA {
651+
"Setting `$funcAVar1 to 'Value set in funcA'"
652+
$funcAVar1 = "Value set in funcA"
653+
funcB
654+
}
631655
632-
PS> $ptest = 2
633-
PS> $ptest
634-
2
635-
```
656+
function funcB {
657+
"In funcB before set -> '$funcAVar1'"
658+
$private:funcAVar1 = "Locally overwrite the value - child scopes can't see me!"
659+
"In funcB after set -> '$funcAVar1'"
660+
funcC
661+
}
636662
637-
Next, create a Sample.ps1 script that contains the following commands. The
638-
command tries to display and change the value of `$ptest`.
663+
function funcC {
664+
"In funcC before set -> '$funcAVar1' - should be the value set in funcA"
665+
$funcAVar1 = "Value set in funcC - Child scopes can see this change."
666+
"In funcC after set -> '$funcAVar1'"
667+
funcD
668+
}
639669
640-
In Sample.ps1:
670+
function funcD {
671+
"In funcD before set -> '$funcAVar1' - should be the value from funcC."
672+
$funcAVar1 = "Value set in funcD"
673+
"In funcD after set -> '$funcAVar1'"
674+
'-------------------'
675+
ShowScopes
676+
}
641677
642-
```powershell
643-
"The value of `$Ptest is $Ptest."
644-
"The value of `$Ptest is $global:Ptest."
678+
function ShowScopes {
679+
$funcAVar1 = "Value set in ShowScopes"
680+
"Scope [0] (local) `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 0 -ValueOnly)'"
681+
"Scope [1] (parent) `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 1 -ValueOnly)'"
682+
"Scope [2] (parent) `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 2 -ValueOnly)'"
683+
"Scope [3] (parent) `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 3 -ValueOnly)'"
684+
"Scope [4] (parent) `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 4 -ValueOnly)'"
685+
}
686+
funcA
687+
# End of ScopeExample.ps1
688+
PS> .\ScopeExample.ps1
645689
```
646690

647-
The `$ptest` variable isn't visible in the script scope, the output is empty.
691+
The output shows the value of the variable in each scope. You can see that the
692+
private variable is only visible in `funcB`, the scope in which it was created.
648693

649-
```powershell
650-
"The value of $Ptest is ."
651-
"The value of $Ptest is ."
652-
```
694+
```Output
695+
Setting $funcAVar1 to 'Value set in funcA'
696+
In funcB before set -> 'Value set in funcA'
697+
In funcB after set -> 'Locally overwrite the value - child scopes can't see me!'
698+
In funcC before set -> 'Value set in funcA' - should be the value set in funcA
699+
In funcC after set -> 'Value set in funcC - Child scopes can see this change.'
700+
In funcD before set -> 'Value set in funcC - Child scopes can see this change.' - should be the value from funcC.
701+
In funcD after set -> 'Value set in funcD'
702+
-------------------
703+
Scope [0] (local) $funcAVar1 = 'Value set in ShowScopes'
704+
Scope [1] (parent) $funcAVar1 = 'Value set in funcD'
705+
Scope [2] (parent) $funcAVar1 = 'Value set in funcC - Child scopes can see this change.'
706+
Scope [3] (parent) $funcAVar1 = 'Locally overwrite the value - child scopes can't see me!'
707+
Scope [4] (parent) $funcAVar1 = 'Value set in funcA'
708+
```
709+
710+
As shown by the output from `ShowScopes`, you can access variables from other
711+
scopes using `Get-Variable` and specifying a scope number.
653712

654713
### Example 5: Using a local variable in a remote command
655714

@@ -675,19 +734,21 @@ The `using` scope modifier was introduced in PowerShell 3.0.
675734

676735
## See also
677736

678-
- [about_Variables][08]
679-
- [about_Environment_Variables][03]
680-
- [about_Functions][04]
681-
- [about_Script_Blocks][07]
682-
- [Start-ThreadJob][10]
737+
- [about_Variables][09]
738+
- [about_Environment_Variables][04]
739+
- [about_Functions][05]
740+
- [about_Script_Blocks][08]
741+
- [Start-ThreadJob][11]
683742

684743
<!-- link references -->
685-
[02]: #private-option
686-
[03]: about_Environment_Variables.md
687-
[04]: about_Functions.md
688-
[05]: about_Operators.md
689-
[06]: about_Remote_Variables.md
690-
[07]: about_Script_Blocks.md
691-
[08]: about_Variables.md
692-
[09]: xref:Microsoft.PowerShell.Core.ForEach-Object
693-
[10]: xref:ThreadJob.Start-ThreadJob
744+
[01]: #example-4-creating-a-private-variable
745+
[02]: #modules
746+
[03]: #private-option
747+
[04]: about_Environment_Variables.md
748+
[05]: about_Functions.md
749+
[06]: about_Operators.md
750+
[07]: about_Remote_Variables.md
751+
[08]: about_Script_Blocks.md
752+
[09]: about_Variables.md
753+
[10]: xref:Microsoft.PowerShell.Core.ForEach-Object
754+
[11]: xref:ThreadJob.Start-ThreadJob

0 commit comments

Comments
 (0)