代码之家  ›  专栏  ›  技术社区  ›  MadBoy

正在加载用于PowerShell模块的其他文件(PS1)

  •  1
  • MadBoy  · 技术社区  · 7 年前

    我已经 PowerShell Module (我们称之为 PSModule )其中我在.psd1中定义如下

    # Script files (.ps1) that are run in the caller's environment prior to importing this module.
    ScriptsToProcess = @('Enums\MessageType.ps1')
    

    它被添加到那里以便文件 MessageType.ps1 那就是正义 Enums 可以跨多个文件重复使用。

    此文件如下:

    enum MessageType {
       None
       Something
    }
    

    一个例子是 PSWriteWord - https://github.com/EvotecIT/PSWriteWord 模块。这应该将枚举保存在它自己的文件夹中,并且在模块启动时仍然加载它们。

    这在大多数情况下都很有效。我运行的脚本没有问题。现在我有了一个奇怪的场景,我创建了:script1.ps1-我称之为。

    script1.ps1的内容

    Import-Module PSModule -Force
    
    Import-Module PSOtherModule -Force
    
    Call-Me -Parameters <params> # part of PSOtherModule
    

    现在在我调用的psoThermodule的call me函数中

    Do-Something
    do-Something
    
    Call-OtherFunction # function from PSModule
    

    它将在ISE或VScode中运行时工作…

    现在,如果我重新运行 Task Scheduler 它不会加载 MessageType 在某种程度上会失败。似乎它只是跳过了 ScriptsToProcess .

    现在,如果你这样做:

    Do-Something
    do-Something
    Import-Module PSModule
    Call-OtherFunction # function from PSModule
    

    它仍然不起作用…但这会…

    Do-Something
    do-Something
    Import-Module PSModule -Force
    Call-OtherFunction # function from PSModule
    

    所以现在我正在努力寻找一种方法,将枚举作为单独的文件添加到我的模块中,并在调用psmodule之前,在不使用import模块的情况下保持运行。

    我发现了这个: https://d-fens.ch/2014/11/26/bug-powershell-scripts-in-scriptstoprocess-attribute-appear-as-loaded-modules/ 它照它所说的做,效果很好,但不能解决我的问题。

    有人知道解决这个问题的方法吗?我尝试将完整路径放入scriptstoprocess,认为scriptstoprocess中的枚举路径可能被某种方式覆盖…但没有。

    my.psm1文件如下:

    #Get public and private function definition files.
    $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
    $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
    $Enums = @( Get-ChildItem -Path $PSScriptRoot\Enums\*.ps1 -ErrorAction SilentlyContinue )
    
    #Dot source the files
    Foreach ($import in @($Public + $Private + $Enums)) {
        Try {
            . $import.fullname
        } Catch {
            Write-Error -Message "Failed to import function $($import.fullname): $_"
    
        }
    }
    Export-ModuleMember -Function '*'
    [string] $ManifestFile = '{0}.psd1' -f (Get-Item $PSCommandPath).BaseName;
    $ManifestPathAndFile = Join-Path -Path $PSScriptRoot -ChildPath $ManifestFile;
    if ( Test-Path -Path $ManifestPathAndFile) {
        $Manifest = (Get-Content -raw $ManifestPathAndFile) | iex;
        foreach ( $ScriptToProcess in $Manifest.ScriptsToProcess) {
            $ModuleToRemove = (Get-Item (Join-Path -Path $PSScriptRoot -ChildPath $ScriptToProcess)).BaseName;
            if (Get-Module $ModuleToRemove) {
                Remove-Module $ModuleToRemove;
            }
        }
    }
    

    我刚刚添加了枚举,但它没有改变任何东西…导出模块之后的事情是从模块中删除脚本(如链接中所示)。我的问题无关紧要。

    可以在这里看到: enter image description here

    2 回复  |  直到 6 年前
        1
  •  2
  •   mklement0    6 年前

    注:

    • 如果你只是在寻找 有效解决方案 基于C代码的按需编译,通过 Add-Type MadBoy's own answer .

    • 如果你愿意的话 了解问题所在 ,包括 为什么? 添加类型 -基于解决方案的帮助,请继续阅读。


    这个 行为如预期 :

    你的 Import-Module PSModule -Force 呼入 script.ps1 点源 Enums\MessageType.ps1 只有 在里面 脚本.ps1 的范围。

    因此,何时 Call-Me 来自模块 PSOtherModule 跑, 对你加载的枚举一无所知 脚本.ps1 ,因为 补骨脂调节剂 有自己的作用域(不从调用方继承)。

    原因是 导入模块PSmodule-强制 在里面 给我打电话 工作就是你强迫 重新加载 模块的 补骨脂调节剂 ,因此点源 枚举\消息类型.ps1 ,使 enum 可用(忽略 -Force 将是no op,因为PowerShell已加载模块(在全局发生会话),因此不处理模块清单及其 ScriptsToProcess 再次进入)。

    对, 使用 添加类型 使用包含按需编译的C代码的字符串可以解决问题 (如所示 the answer you've since posted ,但出于一个不相关的原因: 添加类型 -添加的类型总是 会话全局 无论在什么范围内 添加类型 被调用。

    达到同样的效果 PowerShell 枚举 (或) class )定义 -从而解决你的问题-你需要:

    • 包括 枚举 直接在您的 PSModule.psm1 文件。

    • 使用 using module PSModule 指令而不是 Import-Module PSModule 用于导入模块的Cmdlet调用-仅 using module 使PowerShell基于 枚举 定义在模块外部可用,然后 全球会议 .


    可选背景信息:

    • 枚举 定义是最近添加到PowerShell(v5)中的,它们不是模块导出机制的一部分。

      • 模块外, 枚举 定义是它们在/dot sourcein中定义的范围的本地定义。

      • 如果你 枚举 定义模块的一部分( *.psm1 ) 导入该模块时 使用模块 这些定义确实可用,但总是 全部的 他们中的一员,而且总是 全球会议 (同样) 添加类型 -基于类型,尽管它们甚至 Import-Module )

      • 如果你使用 导入模块 而不是 使用模块 , the 枚举 作为模块一部分的定义是有效的 私有的 .
    • 许多修复和增强 枚举 自Windows PowerShell v5.1/PowerShell Core v6.1.0起,定义处于挂起状态:

      • This meta GitHub issue 跟踪所有这些信息。

      • 制作 导入模块 查看模块 枚举 也讨论了定义 in this issue ;虽然从根本上讲,这是可能的, 导入模块 在执行 运行时 鉴于 使用模块 在执行 分析时间 排除某些用例 导入模块 (简而言之: 引用其他定义 定义)。

      • 单独地,能够明确地控制 哪一个 枚举 将要导出的定义(类似于如何有选择地导出函数、别名和变量)正在讨论中。 in this issue .

        2
  •  1
  •   mklement0    6 年前

    为了解决这个问题,我选择使用 Add-Type 使用按需编译而不是PowerShell自己编译的C代码 enum 构造。它起作用了。

    <#
    enum MessageType {
        Alert
        Cancel
        Disable
        Download
        Minus
        Check
        Add
        None
    }
    
    #>
    
    
    Add-Type -TypeDefinition @"
       public enum MessageType
       {
        Alert,
        Cancel,
        Disable,
        Download,
        Minus,
        Check,
        Add,
        None
       }
    "@
    
    推荐文章