在不继承变量和作用域的情况下从另一个运行一个PowerShell脚本

 2021-04-26 

Run one PowerShell script from another without inheriting variables and scope

通常在PowerShell中可以正常工作:

1
2
3
4
# parent.ps1

$x = 1
&"$PSScriptRoot/child.ps1"
1
2
3
# child.ps1

Write-Host $x

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

我的脚本可以防止这种情况吗?

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

是否可以在不继承作用域的情况下调用child.ps1
还是一种将所有内容标记为私有的方式?


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

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

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

解决方法:

  • 进行中:

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

      • 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功能请求。