模块嵌套级别超出通常是由于在模块导入期间意外进入无限递归(无论您是通过 Import-Module 还是 PSv5+ 导入using module 声明)。
无论您的模块是否有清单,这都可能发生; linked question 的答案显示了它是如何通过清单 发生的;这是一个示例没有:以下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 及其清单,@ 987654340@,应放在名为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+:
- 窗口:
$HOME\Documents\PowerShell\Modules
- Linux、macOS:
$HOME/.local/share/powershell/Modules
为了支持模块发现和自动加载高效并显式控制和指示模块导出的内容,最好显式列出 FunctionsToExport 中的单个导出模块成员,
CmdletsToExport、VariablesToExport 和 AliasesToExport 清单的条目。
为了使模块创建更容易,社区提供了帮助模块:
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