首页 > 解决方案 > 在 psm1 模块内的 Import-Module 上使用“-Force”时规避 PowerShell 模块“隐藏”

问题描述

上下文/设置

我正在开发一个相当大的构建自动化工具,它由 90% 的 PowerShell 代码组成。

许多功能都分组在 .psm1 文件中,这些文件通过使用相互加载

Import-Module "$PSScriptRoot/AnotherModule.psm1" -Force

这对于开发目的/测试代码等似乎是一个好主意,但我最近偶然发现了这种方法的一个问题。

发现/问题

每当加载的模块强制导入先前已在父/外部范围中加载的另一个模块时,在此父/外部范围中不再可以访问它

现在我遇到了一个问题,在脚本开头导入所需的模块并不能保证导入的模块在脚本执行期间实际上是可用的,因为其中一个导入的模块可能会“隐藏”另一个以前加载的模块:-/

例子

假设我们有三个模块:ModA、ModB、ModC:

国防部:

$ErrorActionPreference = "Stop"
Import-Module "$PSScriptRoot\ModB.psm1" -Force
// module A entrails

模组:

$ErrorActionPreference = "Stop"
// module B entrails

国防部:

$ErrorActionPreference = "Stop"
Import-Module "$PSScriptRoot\ModA.psm1" -Force
// module C entrails

以下纠缠测试证明了这种“模块隐藏”行为:

Describe 'powershell module system tests' {

  # ModA -> Import ModB -Force
  # ModC -> Import ModA -Force
  $mod_path = Join-Path $PSScriptRoot "testmod"
  $mods = @("ModA", "ModB", "ModC")

  It "cleanup leftovers from previous tests" {
    Remove-Module $mods -ErrorAction SilentlyContinue
  }

  It 'is able to import ModA, which internally loads module B' {
    Import-Module "$mod_path\ModA.psm1" -Force
    Get-Module ModA | Should -Not -BeNullOrEmpty
    Get-Module ModB | Should -BeNullOrEmpty
  }
  
  It 'is able to import a second module already imported by the first one' {
    Get-Module ModA | Should -Not -BeNullOrEmpty
    Import-Module "$mod_path\ModB.psm1" -Force
    Get-Module ModB | Should -Not -BeNullOrEmpty
  }

  It 'still knows the first module' {
    Get-Module ModA | Should -Not -BeNullOrEmpty
  }

  It 'is able to import a thid module that also imports the first one' {
    Get-Module ModC | Should -BeNullOrEmpty
    Import-Module "$mod_path\ModC.psm1" -Force
    Get-Module ModC | Should -Not -BeNullOrEmpty
  }
  
  It 'does not know A or B any longer, as it has been hidden by C' {
    Get-Module ModA | Should -BeNullOrEmpty
    Get-Module ModB | Should -BeNullOrEmpty
  }

  It 'still knows C' {
    Get-Module ModC | Should -Not -BeNullOrEmpty
  }

  It 'cleanup leftovers from this test' {
    Remove-Module $mods
  }
}

追踪错误

为了查明问题,我试图提出另一个分析代码库并验证在导入另一个模块表时没有从全局模块表中删除“已加载”模块的纠缠测试:

Describe 'verify tool module system hierachy' {
  $tool_modules = Get-ChildItem $script:engine_path -Filter "*.psm1"

  function UNLoadAllToolModules {
    Remove-Module 'Tool-*'
  }
  function LoadAllToolModules {
    $tool_modules | Foreach-Object { Import-Module $_.FullName }
  }

  It "cleanup leftovers from previous tests" {
   UNLoadAllToolModules
  }
  
  It "loading of a module does not hide an already loaded module" {
    $errMods = $()
    $tool_modules | ForEach-Object {
      LoadAllToolModules
      $pre = Get-Module
      $mod = $_
      Import-Module $mod.FullName -Force
      $post = Get-Module
      $mods = Compare-Object $post $pre | Select-Object -Expand InputObject | Select-Object -Expand Name
      $mods | Foreach-Object {
        "import of '$mod' hides '$_'" | Write-Host 
      }
      if ($mods) {
        $errMods += $mod.Name
      }
    }
    $errMods | Should -BeNullOrEmpty
  } 
}

问题

有没有办法打印/调试全局模块表,如此处所示所以我可以弄清楚哪个模块被多次加载/模块树在导入后如何被修改?

解决方案?

目前,解决这个“问题”的唯一方法(除此之外,在其他模块中强制导入模块可能是一个糟糕的设计选择)是在调用此构建自动化套件/工具中包含的模块时删除-force或使用。-globalImport-Module -Force

标签: powershellmoduleimport-module

解决方案


我对此的回答很简单,但可能不适用于您的情况。

PowerShell 中的作用域相当简单。但是,当您开始尝试做这样的事情时,我只是建议您将所有内容加载到全局范围并完成它。否则,你最终会经常追逐你的尾巴。

在最终用户的机器上,使用全局范围是不可接受的(无论如何对我来说)。根据您的情况,在使用全局范围的构建自动化系统上可能是可以接受的,因为这就是它的全部用途。

如果您查看Import-Modulecmdlet 的帮助,您将在-Global参数下看到:

> [!TIP] > You should avoid calling `Import-Module` from within a module.
> Instead, declare the target module as a nested module in the parent module's manifest. 
> Declaring nested modules improves the discoverability of dependencies.

The Global parameter is equivalent to the Scope parameter with a value of Global.

(我修复了格式,所以它在这里工作)。

此建议/提示也可能对您有用。


推荐阅读