为什么我需要模块清单 - 因为已经超过了模块嵌套限制
Why would I need a module manifest - because the module nesting limit has been exceeded
我刚刚在 Powershell 中遇到错误:
because the module nesting limit has been exceeded. Modules can only be nested to 10 levels.
我找到了 并找到了一个叫做 "module manifest" 的东西。我已经有一个模块 .psm1 文件 - 为什么我还需要这个?
注意:我没有10级模块,我有10个模块,全部由一个import.psm1文件加载。
超出 模块嵌套级别通常是在模块导入期间意外进入 无限递归的结果 (无论您是否通过 Import-Module
或 PSv5+ using module
语句导入)。
无论您的模块是否有清单,这都可能发生; 的答案显示了如何通过 清单发生;这是一个 没有 的示例:以下 foo.psm1
模块导致无限递归,导致您看到
的错误消息
# Create sample module (without manifest).
@'
# Accidentally try to import the module itself.
using module .\foo.psm1
function bar { 'hi from module foo' }
'@ > foo.psm1
# This fails, because an infinite import loop is entered,
# eventually causing the nesting limit to be exceeded.
Import-Module .\foo.psm1
为什么使用清单创建(脚本)模块是个好主意:
虽然 模块清单是 可选的 - 独立的 *.psm1
文件本身可以作为 modules - 那里是 使用它们的充分理由:
模块清单是一个 *.psd1
文件,它伴随您的 *.psm1
文件并指定重要的 元数据 ,特别是版本号,以 哈希表文字 的形式;为了获得最佳的用户体验,两个文件都应放在同名的目录中(例如, Foo.psm1
及其清单 Foo.psd1
应放置在名为 Foo
).
的目录中
通过使用显化模块,您启用了几个重要的用例:
您需要一个清单来正确支持模块的软件开发过程,尤其是版本管理。
- 它也是支持并排安装模块多个版本的先决条件。
您需要一个清单来自动加载相关资源,例如其他模块或辅助 .NET 程序集,并定义帮助资源。
您需要一个清单以便与 PowerShell 集成 module auto-loading mechanism:如果您将正确显示的模块放入 $env:PSModulePath
中列出的目录之一,PowerShell 将:
- 甚至在导入模块之前就发现模块及其命令。
- 将在您第一次尝试从会话中调用命令时按需导入。
您需要清单才能将模块发布到 PowerShell 模块的官方在线存储库,PowerShell Gallery
快速概述使用清单创建模块的步骤:
- 创建一个目录,以您的
.psm1
文件的基本名称命名;例如,Foo
- 将脚本代码作为文件
Foo.psm1
文件放在该目录中。
在同一目录中,使用 New-ModuleManifest
cmdlet,创建具有相同基本名称(例如 Foo.psd1
)的清单 .psd1
文件
至少,更新新 .psd1
文件中的 RootModule
条目以指向您的 .psm1
文件(例如,RootModule = 'Foo.psm1'
)
要与自动加载功能集成,请将您的模块目录放在 $env:PSModulePath
中列出的位置之一;对于当前用户,该位置是:
- Windows PowerShell:
$HOME\Documents\WindowsPowerShell\Modules
- PowerShell [核心] v6+:
- Windows:
$HOME\Documents\PowerShell\Modules
- Linux,macOS:
$HOME/.local/share/powershell/Modules
有效地支持模块发现和自动加载并显式控制和指示模块导出的内容,最好 明确列出 FunctionsToExport
中的各个导出模块成员,
CmdletsToExport
、VariablesToExport
和 AliasesToExport
个清单条目。
为了使模块创建更容易,社区提供了辅助模块:
Plaster
是一个 "template-based file and project generator written in PowerShell",也可用于脚手架模块:
内置的 "New PowerShell Manifest Module" 模板搭建了一个模块目录,其中包含所有必要的文件并支持 Pester
测试。
请参阅 this blog post 了解演练。
Stucco
以 Plaster 为基础提供 "opinionated Plaster template for building high-quality PowerShell modules."
- Stucco 是一种高级工具,它超越了单纯的模块创建,它通过搭建包括
psake
任务的整个项目结构、CI/CD 集成的搭建、许可和帮助创作。
Plaster
的快速示例:
# Install Plaster for the current user, if necessary.
Install-Module Plaster -Scope CurrentUser
# Get the template for creating a new script module.
$template = Get-PlasterTemplate | Where TemplatePath -match ScriptModule
# Scaffold a module in subdirectory 'Foo'
# * This will present a series of prompts, most of them with default values.
# * IMPORTANT: Be sure to also choose 'Foo' as the module *name* when prompted,
# so that the module auto-loading feature can discover your module
# (if placed in a dir. in $env:PSModulePath) and also so that you
# you can load it by its *directory* path; e.g., Import-Module ./Foo
Invoke-Plaster -TemplatePath $template.TemplatePath -Destination Foo
# Add a test function to the `.psm1` file.
# Note:
# * This is just for illustrative purposes. In real life, you would
# obviously use an editor to add functions to your module.
# * The function must be placed *before* the `Export-ModuleMember` call in order
# to be exported.
# * As stated, it is additionally recommended to list the exported members
# *explicitly*, one by one, in the *ToExport keys of the *.psd1 file.
(Get-Content -Raw ./Foo/Foo.psm1) -replace '\r?\n\r?\n', "`n`nfunction Get-Foo { 'Hi from module Foo.' }`n" | Set-Content -Encoding utf8 ./Foo/Foo.psm1
# Import the newly created module by its *directory* path.
# IMPORTANT:
# As stated, this assumes that you specified 'Foo' as the module name, i.e.
# that your manifest's file name is 'Foo.psd1', and your script module's
# 'Foo.psm1'.
Import-Module ./Foo -Verbose -Force
'---'
# Call the test function
Get-Foo
'---'
# Invoke the module's tests.
# Note: The scaffolding creates a single test to ensure that the
# module manifest (*.psd1) is valid.
Invoke-Pester ./Foo
您应该会看到如下输出:
VERBOSE: Loading module from path 'C:\Users\jdoe\Foo\Foo.psd1'.
VERBOSE: Loading module from path 'C:\Users\jdoe\Foo\Foo.psm1'.
VERBOSE: Importing function 'Get-Foo'.
---
Hi from module Foo.
---
____ __
/ __ \___ _____/ /____ _____
/ /_/ / _ \/ ___/ __/ _ \/ ___/
/ ____/ __(__ ) /_/ __/ /
/_/ \___/____/\__/\___/_/
Pester v4.9.0
Executing all tests in './Foo'
Executing script C:\Users\jdoe\Foo\test\Foo.Tests.ps1
Describing Module Manifest Tests
[+] Passes Test-ModuleManifest 128ms
Tests completed in 375ms
Tests Passed: 1, Failed: 0, Skipped: 0, Pending: 0, Inconclusive: 0
我刚刚在 Powershell 中遇到错误:
because the module nesting limit has been exceeded. Modules can only be nested to 10 levels.
我找到了
注意:我没有10级模块,我有10个模块,全部由一个import.psm1文件加载。
超出 模块嵌套级别通常是在模块导入期间意外进入 无限递归的结果 (无论您是否通过 Import-Module
或 PSv5+ using module
语句导入)。
无论您的模块是否有清单,这都可能发生; foo.psm1
模块导致无限递归,导致您看到
# Create sample module (without manifest).
@'
# Accidentally try to import the module itself.
using module .\foo.psm1
function bar { 'hi from module foo' }
'@ > foo.psm1
# This fails, because an infinite import loop is entered,
# eventually causing the nesting limit to be exceeded.
Import-Module .\foo.psm1
为什么使用清单创建(脚本)模块是个好主意:
虽然 模块清单是 可选的 - 独立的 *.psm1
文件本身可以作为 modules - 那里是 使用它们的充分理由:
模块清单是一个 *.psd1
文件,它伴随您的 *.psm1
文件并指定重要的 元数据 ,特别是版本号,以 哈希表文字 的形式;为了获得最佳的用户体验,两个文件都应放在同名的目录中(例如, Foo.psm1
及其清单 Foo.psd1
应放置在名为 Foo
).
通过使用显化模块,您启用了几个重要的用例:
您需要一个清单来正确支持模块的软件开发过程,尤其是版本管理。
- 它也是支持并排安装模块多个版本的先决条件。
您需要一个清单来自动加载相关资源,例如其他模块或辅助 .NET 程序集,并定义帮助资源。
您需要一个清单以便与 PowerShell 集成 module auto-loading mechanism:如果您将正确显示的模块放入
$env:PSModulePath
中列出的目录之一,PowerShell 将:- 甚至在导入模块之前就发现模块及其命令。
- 将在您第一次尝试从会话中调用命令时按需导入。
您需要清单才能将模块发布到 PowerShell 模块的官方在线存储库,PowerShell Gallery
快速概述使用清单创建模块的步骤:
- 创建一个目录,以您的
.psm1
文件的基本名称命名;例如,Foo
- 将脚本代码作为文件
Foo.psm1
文件放在该目录中。 在同一目录中,使用
New-ModuleManifest
cmdlet,创建具有相同基本名称(例如Foo.psd1
)的清单.psd1
文件至少,更新新
.psd1
文件中的RootModule
条目以指向您的.psm1
文件(例如,RootModule = 'Foo.psm1'
)要与自动加载功能集成,请将您的模块目录放在
$env:PSModulePath
中列出的位置之一;对于当前用户,该位置是:- Windows PowerShell:
$HOME\Documents\WindowsPowerShell\Modules
- PowerShell [核心] v6+:
- Windows:
$HOME\Documents\PowerShell\Modules
- Linux,macOS:
$HOME/.local/share/powershell/Modules
- Windows:
- Windows PowerShell:
有效地支持模块发现和自动加载并显式控制和指示模块导出的内容,最好 明确列出
FunctionsToExport
中的各个导出模块成员,CmdletsToExport
、VariablesToExport
和AliasesToExport
个清单条目。
为了使模块创建更容易,社区提供了辅助模块:
Plaster
是一个 "template-based file and project generator written in PowerShell",也可用于脚手架模块:内置的 "New PowerShell Manifest Module" 模板搭建了一个模块目录,其中包含所有必要的文件并支持
Pester
测试。请参阅 this blog post 了解演练。
Stucco
以 Plaster 为基础提供 "opinionated Plaster template for building high-quality PowerShell modules."- Stucco 是一种高级工具,它超越了单纯的模块创建,它通过搭建包括
psake
任务的整个项目结构、CI/CD 集成的搭建、许可和帮助创作。
- Stucco 是一种高级工具,它超越了单纯的模块创建,它通过搭建包括
Plaster
的快速示例:
# Install Plaster for the current user, if necessary.
Install-Module Plaster -Scope CurrentUser
# Get the template for creating a new script module.
$template = Get-PlasterTemplate | Where TemplatePath -match ScriptModule
# Scaffold a module in subdirectory 'Foo'
# * This will present a series of prompts, most of them with default values.
# * IMPORTANT: Be sure to also choose 'Foo' as the module *name* when prompted,
# so that the module auto-loading feature can discover your module
# (if placed in a dir. in $env:PSModulePath) and also so that you
# you can load it by its *directory* path; e.g., Import-Module ./Foo
Invoke-Plaster -TemplatePath $template.TemplatePath -Destination Foo
# Add a test function to the `.psm1` file.
# Note:
# * This is just for illustrative purposes. In real life, you would
# obviously use an editor to add functions to your module.
# * The function must be placed *before* the `Export-ModuleMember` call in order
# to be exported.
# * As stated, it is additionally recommended to list the exported members
# *explicitly*, one by one, in the *ToExport keys of the *.psd1 file.
(Get-Content -Raw ./Foo/Foo.psm1) -replace '\r?\n\r?\n', "`n`nfunction Get-Foo { 'Hi from module Foo.' }`n" | Set-Content -Encoding utf8 ./Foo/Foo.psm1
# Import the newly created module by its *directory* path.
# IMPORTANT:
# As stated, this assumes that you specified 'Foo' as the module name, i.e.
# that your manifest's file name is 'Foo.psd1', and your script module's
# 'Foo.psm1'.
Import-Module ./Foo -Verbose -Force
'---'
# Call the test function
Get-Foo
'---'
# Invoke the module's tests.
# Note: The scaffolding creates a single test to ensure that the
# module manifest (*.psd1) is valid.
Invoke-Pester ./Foo
您应该会看到如下输出:
VERBOSE: Loading module from path 'C:\Users\jdoe\Foo\Foo.psd1'.
VERBOSE: Loading module from path 'C:\Users\jdoe\Foo\Foo.psm1'.
VERBOSE: Importing function 'Get-Foo'.
---
Hi from module Foo.
---
____ __
/ __ \___ _____/ /____ _____
/ /_/ / _ \/ ___/ __/ _ \/ ___/
/ ____/ __(__ ) /_/ __/ /
/_/ \___/____/\__/\___/_/
Pester v4.9.0
Executing all tests in './Foo'
Executing script C:\Users\jdoe\Foo\test\Foo.Tests.ps1
Describing Module Manifest Tests
[+] Passes Test-ModuleManifest 128ms
Tests completed in 375ms
Tests Passed: 1, Failed: 0, Skipped: 0, Pending: 0, Inconclusive: 0