Andrey Shchekin
Andrey Shchekin

Reputation: 21609

Run one PowerShell script from another without inheriting variables and scope

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

Answers (1)

mklement0
mklement0

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:

    • Call your script via a new PowerShell process (powershell -File ... or pwsh -File ...) or via a background job (using Start-Job).
      • Caveat: In addition to decreased performance, due to cross-process XML-serialized serialization type fidelity may be lost - see this answer for details.

[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

Related Questions