从一个 PowerShell 脚本运行另一个脚本而不继承变量和范围

And*_*kin 5 powershell

通常在 PowerShell 中这是有效的:

# parent.ps1

$x = 1
&"$PSScriptRoot/child.ps1"
Run Code Online (Sandbox Code Playgroud)
# child.ps1

Write-Host $x
Run Code Online (Sandbox Code Playgroud)

运行时parent.ps1,它会打印出1sincechild.ps1已经继承了它。

我可以在我的脚本中阻止这种情况吗?

我可以这样做$private:x = 1,但是父级有很多变量,所以它很冗长并且容易出错。

有没有一种方法可以child.ps1在不继承范围的情况下调用?或者也许是一种将父级中的所有内容
标记为私有的方法?

mkl*_*nt0 4

不,除非使用作用域定义调用作用域(及其祖先作用域)中的所有变量$private:,否则无法阻止 PowerShell 的动态作用域。

也就是说,在给定作用域(不带$private:)中创建变量使其对其所有后代作用域可见,例如脚本(直接调用或通过 调用&)运行的子作用域。

此外,某些自动(内置)变量是用 option 定义的,这总是使它们在所有AllScope作用域中可见,而不仅仅是后代作用域。

解决方法

  • 进程中:

    • 使用(PowerShell v6+) 或(v7+)通过线程作业调用脚本;例如:Start-ThreadJobForEach-Object -Parallel

      • ForEach-Object -Parallel { $PSScriptRoot/child.ps1 }

      • 线程作业及其创建的线程ForEach-Object -Parallel不会继承调用者的状态(v7+ 中的当前位置除外)[1]

    • 在脚本开始时,通过枚举所有变量Get-Variable并创建您显式设置的本地副本$null(您需要忽略由无法覆盖的内置变量引起的错误) - 这将有效地隐藏祖先作用域中的变量。

  • 进程外:

    • 通过新的 PowerShell 进程(powershell -File ...pwsh -File ...)或通过后台作业(使用Start-Job)调用您的脚本。
      • 警告:除了性能下降之外,由于跨进程 XML 序列化,序列化类型保真度可能会丢失 - 有关详细信息,请参阅此答案

[1] 请注意,现在正在考虑提供将调用者状态复制到线程的选择;ForEach-Object -Parallel请参阅此 GitHub 功能请求