当一组函数会被多个脚本复用、需要版本控制、需要分发到不同机器,或要安装第三方能力(如 Az、Pester)时,用模块而不是复制 .ps1 文件。排查“命令为什么找不到、加载的是哪个版本、为什么和文档不一致”也依赖模块知识。
一次性脚本不需要模块化;把几十行函数硬做成模块只会增加清单和发布成本。模块适合有稳定接口、需要长期维护的逻辑。
.psm1,清单是 .psd1,二进制模块是 .dll,还有嵌套模块和复合模块。清单描述版本、作者、依赖、导出命令和兼容的 PowerShell 版本。$env:PSModulePath 是一组目录,按顺序搜索;用户目录优先于系统目录,同名命令先命中靠前的模块。Import-Module 显式加载,命令首次被调用时也会自动加载 PSModulePath 中的模块。Export-ModuleMember 或清单的 FunctionsToExport、AliasesToExport、CmdletsToExport 控制边界,未导出的函数只在模块作用域内可用。-RequiredVersion 精确加载、-MinimumVersion 限定下限;脚本头部用 #Requires -Modules 声明依赖,让缺依赖时尽早失败。Find-Module、Install-Module、Save-Module;7.4 起内置 PSResourceGet,优先 Install-PSResource。安装前确认仓库和发布者,不要默认信任任意源。Documents\WindowsPowerShell\Modules,7.x 是 Documents\PowerShell\Modules。7.x 会保留系统级 5.1 模块路径(如 %ProgramFiles%\WindowsPowerShell\Modules、%SystemRoot%\System32\WindowsPowerShell\v1.0\Modules)以兼容旧模块,但不会自动把 5.1 的用户级 Documents\WindowsPowerShell\Modules 加入搜索路径;需要时手动加入 PSModulePath。using module 引入的类型绑定具体运行时:5.1 是 .NET Framework,7.x 是 .NET,不能假设同一个 DLL 两边都能加载。# 1. 看清模块搜索路径和顺序,Windows 用分号分隔,Linux/macOS 用冒号
PS C:\> $env:PSModulePath -split [IO.Path]::PathSeparator
# 2. 列出已安装模块及版本,排查“装了但加载错版本”
PS C:\> Get-Module -ListAvailable | Sort-Object Name, Version | Select-Object Name, Version, Path
PS C:\> Get-Module -ListAvailable Pester
# 3. 显式导入清单并查看它导出了什么
PS C:\> Import-Module .\MyModule.psd1 -Force -Verbose
PS C:\> Get-Command -Module MyModule
# 4. 不写 Import-Module 也能调用,首次调用会从 PSModulePath 自动加载
PS C:\> Get-Widget -Name api
PS C:\> Get-Module MyModule
# 5. 在脚本头部声明依赖,缺模块或版本不够时不继续执行
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' }
# 6. 查找、安装到当前用户、离线保存(5.1 的 PowerShellGet 写法)
PS C:\> Find-Module -Name Pester
PS C:\> Install-Module -Name Pester -Scope CurrentUser -MinimumVersion 5.0
PS C:\> Save-Module -Name Pester -Path C:\offline\modules
# 7. PowerShell 7.4+ 用 PSResourceGet,参数更清晰
PS C:\> Install-PSResource -Name Pester -Scope CurrentUser
# 8. 脚本模块的最小导出示例
# MyModule.psm1
function Get-Widget { param([string] $Name) [pscustomobject]@{ Name = $Name } }
Export-ModuleMember -Function Get-Widget
Install-Module 可能要求安装 NuGet 提供程序并信任 PSGallery;这是安装来源问题,不是网络坏了。先看提示,再决定是否信任该源。Import-Module -Force 能重新加载脚本模块,但已经存在的对象引用不会自动更新;类类型还需要在脚本顶部用 using module,普通 Import-Module 不足以让类型在解析阶段可见。Import-Module 的默认选择可能不同。排查时同时看 Get-Module、Get-Module -ListAvailable 和 Get-Command -Module,不要只看文件名。FunctionsToExport = '*' 会把内部辅助函数也暴露出去,后续重构容易破坏调用方;显式列出导出项,内部函数保持未导出。Import-Module 并固定版本。PSModulePath 时要谨慎覆盖合上页面,先回答,再展开对照。
Get-Module 里一开始看不到对应模块,为什么?Import-Module 后仍然报“找不到类型”?Get-Module 才能看到它;这不代表模块此前已被显式导入。Import-Module MyModule -RequiredVersion 1.2.0 精确加载,并在脚本里用 #Requires -Modules 声明版本要求;同时确认 PSModulePath 中没有更高优先级的同名模块。Import-Module 发生在运行时;要在脚本或模块顶部使用 using module,并确保清单正确导出该模块。
小程序版
微信扫码,随时查命令