如何检查PowerShell模块是否已安装?

147

为了检查一个模块是否存在,我尝试了以下方法:

try {
    Import-Module SomeModule
    Write-Host "Module exists"
} 
catch {
    Write-Host "Module does not exist"
}

输出结果为:

Import-Module : The specified module 'SomeModule' was not loaded because no valid module file was found in any module directory.
At D:\keytalk\Software\Client\TestProjects\Export\test.ps1:2 char:5
+     Import-Module SomeModule
+     ~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (SomeModule:String) [Import-Module], FileNotFoundException
    + FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand

Module exists

我确实遇到了错误,但没有抛出异常,因此我们最终看到了“Module exists”,尽管“SomeModule”不存在。

是否有一种好的方法(最好不会生成错误)来检测PowerShell模块是否已在系统上安装?


1
查看Windows PowerShell中已加载和可用的模块 提示:显示已加载和可用的Windows PowerShell模块 - Michael Freidgeim
18个回答

183
你可以使用 Get-Module 命令的 ListAvailable 选项:
if (Get-Module -ListAvailable -Name SomeModule) {
    Write-Host "Module exists"
} 
else {
    Write-Host "Module does not exist"
}

1
我本来想建议: Import-Module NonexistingModule -ErrorAction SilentlyContinue IF($error){Write-Host '模块不存在'} ELSE{Write-Host '模块存在'} 但是你的方法更好,更优雅 :) - Erik Blomgren
这很好用。谢谢。我会使用 Write-Warning "Module does not exist..." ;Break 但你已经完成了所有的艰苦工作。 - Craig.C
1
如果您正在使用Import-Module和自定义dll文件导入库,请不要使用-ListAvailable选项来确定模块是否已安装,因为它不会列出。根据PowerShell 6文档,“ListAvailable不返回有关未在PSModulePath环境变量中找到的模块的信息,即使这些模块在当前会话中加载”。 - Dave F
这并不确定一个模块是否已经被安装(即 Import-Module),它只是确定该模块是否可以立即安装,而无需指定 $env:PSModulePath 中不存在的特定位置。 - Slogmeister Extraordinaire

53

模块可以处于以下状态:

  • 已导入
  • 可用于磁盘(或本地网络)
  • 可在在线库中使用

如果你只是想在PowerShell会话中使用该模块,以下函数将使其可用,或者如果无法完成此操作,则退出:

function Load-Module ($m) {

    # If module is imported say that and do nothing
    if (Get-Module | Where-Object {$_.Name -eq $m}) {
        write-host "Module $m is already imported."
    }
    else {

        # If module is not imported, but available on disk then import
        if (Get-Module -ListAvailable | Where-Object {$_.Name -eq $m}) {
            Import-Module $m -Verbose
        }
        else {

            # If module is not imported, not available on disk, but is in online gallery then install and import
            if (Find-Module -Name $m | Where-Object {$_.Name -eq $m}) {
                Install-Module -Name $m -Force -Verbose -Scope CurrentUser
                Import-Module $m -Verbose
            }
            else {

                # If the module is not imported, not available and not in the online gallery then abort
                write-host "Module $m not imported, not available and not in an online gallery, exiting."
                EXIT 1
            }
        }
    }
}

Load-Module "ModuleName" # Use "PoshRSJob" to test it out

2
这是一个非常好的“一站式”解决方案(我只是将Load-Module更改为返回$true/$false而不是EXIT)。 - Andrzej Martyna
这很美。 - sean
这样做似乎大大降低了我的个人资料速度。现在加载个人资料需要超过2秒钟。 - John
我在 macOS 上的 PowerShell 7.3.1 上成功测试了这个。干得好,Rod! - Van Vangor
很棒的回答!还有一个状态 - 模块可以通过 Save-Module 进行“保存”,但在 Get-Module -ListAvailable 中不可用。例如,Microsoft 在 Microsoft 托管的 Azure DevOps 代理 上保存了一些模块在 C:\Modules 中。 - oderibas

36

ListAvailable选项对我无效。相反,使用以下方法:

if (-not (Get-Module -Name "<moduleNameHere>")) {
    # module is not loaded
}

换言之,更简洁地说:

if (!(Get-Module "<moduleNameHere>")) {
    # module is not loaded
}

@oɔɯǝɹ 我以为-ListAvailable不可用,但我仍在尝试Import-Module。使用Get-Module就可以了。 - Craig.C
4
您检查模块是否已加载(这本身非常有用- http://www.systemcentercentral.com/powershell-tip-how-to-load-if-a-module-is-loaded-before-attempting-import-module-3/),但另一个答案则检查模块是否存在。 - Michael Freidgeim
1
这比使用ListAvailable要快得多。 - GaTechThomas
相当确定 ! 在 PowerShell 中不起作用,这取决于版本? - Kellen Stuart
2
@KolobCanyon !-not 的别名,但我通常不建议在 ps1 脚本中使用别名。@GaTechThomas 它也有不同的行为,正如 @MichaelFreidgeim 指定的那样(它不会为已安装但未导入的模块返回真值)。 - AndreasHassing
我无法通过Core使用这个脚本;然而,使用“-ListAvailable”的被接受答案却非常有效。 - CodeBreaker

27
当前版本的Powershell有一个Get-InstalledModule函数,非常适合此目的(或者至少在我的情况下是这样的)。

Get-InstalledModule

描述

Get-InstalledModule cmdlet获取安装在计算机上的PowerShell模块。

唯一的问题是,如果请求的模块不存在,它会抛出异常,因此我们需要适当设置ErrorAction来抑制该情况。
if ((Get-InstalledModule `
    -Name "AzureRm.Profile" `
    -MinimumVersion 5.0 ` # Optionally specify minimum version to have
    -ErrorAction SilentlyContinue) -eq $null) {

    # Install it...
}

2
Get-InstalledModule 仅考虑通过 Install-Module 安装的模块,而 Get-Module -List 将考虑所有可用的模块,无论它们是如何安装的。 - gordy

18

您可以使用 Get-InstalledModule 命令。

If (-not(Get-InstalledModule SomeModule -ErrorAction silentlycontinue)) {
  Write-Host "Module does not exist"
}
Else {
  Write-Host "Module exists"
}

2
这里有许多好的答案,但是使用这种新的简单方法,这可能应该成为新的被接受的答案。 - not2qubit
2
只有在安装了“PowerShellGet”时,此方法才有效。在某些无网络连接的服务器上可能没有安装该程序。 - Diti

16

我重新回顾这个问题,因为我刚刚遇到了这个问题,并且答案中存在一些不正确的东西(尽管在评论中提到过)。

首先,原始问题问如何确定 PowerShell 模块是否已安装。我们需要谈论一下“已安装”这个词!你不会像安装软件那样传统地安装 PowerShell 模块。

PowerShell 模块可以是可用的(即它们在 PowerShell 模块路径上),也可以是已导入的(它们已导入您的会话并且您可以调用其中包含的函数)。这是检查模块路径的方法,以防您想知道要将模块存储在哪里:

$env:psmodulepath

我认为由于所有用户都可以使用,使用C:\Program Files\WindowsPowerShell\Modules;变得越来越普遍,但是如果您想要锁定自己的模块以供自己使用,请将它们包含在您的配置文件中。C:\Users%username%\Documents\WindowsPowerShell\Modules;

好了,回到两个状态。

该模块是否可用(使用“可用”来表示在原始问题中已安装)?

Get-Module -Listavailable -Name <modulename>

这将告诉您模块是否可供导入。

该模块是否已导入?(我使用它作为原始问题中单词“存在”的答案。)

Get-module -Name <modulename>

如果未导入该模块,则此操作将返回空结果;如果已导入,则会返回该模块的一行简短描述。像往常一样,在 Stack Overflow 上,尝试在自己的模块上执行上述命令。


1
你可以在PowerShell中安装模块。PowerShellGet有一个名为Get-InstalledModule的命令,它返回的输出与Get-Module -ListAvailable不同。 - Igor

16

当我在我的脚本中使用非默认模块时,我会调用下面的函数。除了模块名称外,您还可以提供一个最低版本。

# See https://www.powershellgallery.com/ for module and version info
Function Install-ModuleIfNotInstalled(
    [string] [Parameter(Mandatory = $true)] $moduleName,
    [string] $minimalVersion
) {
    $module = Get-Module -Name $moduleName -ListAvailable |`
        Where-Object { $null -eq $minimalVersion -or $minimalVersion -lt $_.Version } |`
        Select-Object -Last 1
    if ($null -ne $module) {
         Write-Verbose ('Module {0} (v{1}) is available.' -f $moduleName, $module.Version)
    }
    else {
        Import-Module -Name 'PowershellGet'
        $installedModule = Get-InstalledModule -Name $moduleName -ErrorAction SilentlyContinue
        if ($null -ne $installedModule) {
            Write-Verbose ('Module [{0}] (v {1}) is installed.' -f $moduleName, $installedModule.Version)
        }
        if ($null -eq $installedModule -or ($null -ne $minimalVersion -and $installedModule.Version -lt $minimalVersion)) {
            Write-Verbose ('Module {0} min.vers {1}: not installed; check if nuget v2.8.5.201 or later is installed.' -f $moduleName, $minimalVersion)
            #First check if package provider NuGet is installed. Incase an older version is installed the required version is installed explicitly
            if ((Get-PackageProvider -Name NuGet -Force).Version -lt '2.8.5.201') {
                Write-Warning ('Module {0} min.vers {1}: Install nuget!' -f $moduleName, $minimalVersion)
                Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Scope CurrentUser -Force
            }        
            $optionalArgs = New-Object -TypeName Hashtable
            if ($null -ne $minimalVersion) {
                $optionalArgs['RequiredVersion'] = $minimalVersion
            }  
            Write-Warning ('Install module {0} (version [{1}]) within scope of the current user.' -f $moduleName, $minimalVersion)
            Install-Module -Name $moduleName @optionalArgs -Scope CurrentUser -Force -Verbose
        } 
    }
}

使用示例:

Install-ModuleIfNotInstalled 'CosmosDB' '2.1.3.528'

如果这个对你有用的话,请让我知道(或者不有用)


8
您可以使用 #Requires 语句(支持来自 PowerShell 3.0 的模块)。

#Requires 语句会阻止脚本运行,除非满足 PowerShell 版本、模块、插件以及模块和插件版本的先决条件。

因此,在脚本顶部添加 #Requires -Module <ModuleName> 即可。

如果所需的模块不在当前会话中,则 PowerShell 会导入它们。

如果无法导入模块,则 PowerShell 会抛出终止错误。


2
对于那些认为这是比上面更长的函数更干净的解决方案的其他人,请注意:如果当前会话中没有该模块,则会导入该模块,但它不会从互联网源安装它。因此,如果您要将其部署到可能已经安装了该模块的机器上,您可能需要/需要更长的解决方案。 - Adam Nofsinger

7
try {
    Import-Module SomeModule
    Write-Host "Module exists"
} 
catch {
    Write-Host "Module does not exist"
}

应该指出,您的cmdlet Import-Module没有终止错误,因此异常未被捕获,因此无论您的catch语句如何,都不会返回您编写的新语句。

(https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-6

从上面可以看出:

“终止错误会停止语句的运行。如果 PowerShell 无法以某种方式处理终止错误,则 PowerShell 也会停止使用当前管道运行函数或脚本。在其他语言中,如 C#,终止错误称为异常。有关错误的更多信息,请参阅 about_Errors。”

应该写成:

Try {
    Import-Module SomeModule -Force -Erroraction stop
    Write-Host "yep"
}
Catch {
    Write-Host "nope"
}

返回:

nope

如果你想要更加彻底,你应该添加其他建议的 cmdlets Get-Module -ListAvailable -NameGet-Module -Name,在运行其他函数/cmdlets 之前要特别小心。如果是从 ps gallery 或其他地方安装的,还可以运行 Find-Module 命令来查看是否有新版本可用。


5

我认为,在检查模块是否已存在时,有以下两种情况:

1)已安装

2)已导入

要检查是否已安装:

选项1:使用Get-Module命令和-ListAvailable参数:

If(Get-Module -ListAvailable -Name "<ModuleName>"){'Module is installed'}
Else{'Module is NOT installed'}

选项2:使用$error对象:
$error.clear()
Import-Module "<ModuleName>" -ErrorAction SilentlyContinue
If($error){Write-Host 'Module is NOT installed'}
Else{Write-Host 'Module is installed'}

检查是否已导入:

使用带有 -Name 参数的 Get-Module 命令(如果需要,可以省略该参数,因为它是默认值):

if ((Get-Module -Name "<ModuleName>")) {
   Write-Host "Module is already imported (i.e. its cmdlets are available to be used.)"
}
else {
   Write-Warning "Module is NOT imported (must be installed before importing)."
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接