Reputation: 21609
Normally in PowerShell this works:
# parent.ps1
$x = 1
&"$PSScriptRoot/child.ps1"
# child.ps1
Write-Host $x
When parent.ps1
runs, it prints out 1
since child.ps1
has inherited it.
Can I prevent this for my script?
I can do $private:x = 1
, but parent has many variables, so it's verbose and error-prone.
Is there a way to call child.ps1
without inheriting scope?
Or maybe a way to mark everything in parent private?
Upvotes: 5
Views: 1242
Reputation: 439607
No, short of defining all variables in the calling scope (and its ancestral scopes) with the $private:
scope, you cannot prevent PowerShell's dynamic scoping.
That is, creating a variable in a given scope (without $private:
) makes it visible to all its descendant scopes, such as the child scope in which a script (invoked directly or via &
) runs.
Also, certain automatic (built-in) variable are defined with option AllScope
, which invariably makes them visible in all scopes, not just descendant ones.
Workarounds:
In-process:
Call your script via a thread job, using Start-ThreadJob
(PowerShell v6+) or with ForEach-Object -Parallel
(v7+); e.g.:
ForEach-Object -Parallel { $PSScriptRoot/child.ps1 }
Thread jobs and the threads created by ForEach-Object -Parallel
do not inherit the caller's state (with the exception of the current location in v7+)[1].
At the start of your script, enumerate all variables via Get-Variable
and create local copies that you explicitly set to $null
(you'll need to ignore errors stemming from built-in variables that you cannot override) - this will effectively shadow the variables from ancestral scopes.
Out-of-process:
powershell -File ...
or pwsh -File ...
) or via a background job (using Start-Job
).
[1] Note that providing an opt-in for copying the caller's state to the ForEach-Object -Parallel
threads is now being considered; see this GitHub feature request.
Upvotes: 5