5

给定一个 ps1 文件作为具有以下代码的模块的一部分:

function Get-Greeting {
    'Hello {0}' -f $Env:Username
}
Export-ModuleMember -Function:Get-Greeting

当作为模块的一部分加载时,一切都很好。如果我点源脚本,我会得到

Export-ModuleMember : The Export-ModuleMember cmdlet can only be called from inside a module.

我知道我可以在 Export-ModuleMember 上添加一个 -ErrorAction:Ignore,但这不是重点。我想让一个脚本以不同的方式运行,无论它是导入的还是点源的。

$PSScriptRoot在版本 2 中,人们可能会围绕$PSScriptRoot. 我尝试查看 中的各种项目$MyInvocation,但要么我错过了一些东西,要么它没有任何用处。

我也尝试在Get-Variable模块内部和外部运行,但再次发现没有差异。

Import-Module当我运行 as vs时,我错过了什么不同的地方. myscript.ps1

4

3 回答 3

3

看来我找到了答案。我在 中遗漏了一些东西$MyInvocation,因为我在寻找错误的范围。给定以下文件:

# .\moduleDetection\moduleDetection.ps1
$ErrorActionPreference = 'SilentlyContinue'; 
'=== Parent Invocation.MyCommand: [{0}]' -f (Get-Variable -Name:MyInvocation -Scope:1 -ValueOnly | Select -Expand MyCommand) | Out-Host;

.

# .\moduleDotSource\moduleDotSource.psm1
. "$PSScriptRoot\..\moduleDetection\moduleDetection.ps1"

.

# .\module-test.ps1
$Error.Clear()

Write-Host "Powershell Version 3:"
Write-Host "Powershell -Command Import-Module (direct)"
powershell -nologo -noprofile -Command { Import-Module .\moduledetection }

Write-Host "Powershell -Command Import-Module (dot-source)"
powershell -nologo -noprofile -Command { Import-Module .\moduleDotSource }

Write-Host "Powershell -File ...moduledetection.ps1"
powershell -nologo -noprofile -File .\moduledetection\moduleDetection.ps1

Write-Host "Powershell Dot-Source"
powershell -nologo -noprofile -Command { . .\moduledetection\moduleDetection.ps1 }

Write-Host ""
Write-Host "Powershell Version 2:"
Write-Host "Powershell -Version 2 -Command Import-Module"
powershell -version 2.0 -nologo -noprofile -Command { Import-Module .\moduledetection }

Write-Host "Powershell -Version 2 -Command Import-Module (dot-source)"
powershell -version 2.0 -nologo -noprofile -Command { Import-Module .\moduleDotSource }

Write-Host "Powershell -Version 2 -File ...moduledetection.ps1"
powershell -version 2.0 -nologo -noprofile -File .\moduledetection\moduleDetection.ps1

Write-Host "Powershell -Version 2 Dot-Source"
powershell -version 2.0 -nologo -noprofile -Command { . .\moduledetection\moduleDetection.ps1 }

最后,创建一个从原始 ps1 到具有正确名称的 psm1 的符号链接,以作为直接模块加载。

cmd /c mklink .\moduledetection\moduleDetection.ps1 .\moduledetection\moduleDetection.psm1

输出显示父范围具有密钥。

输出:

Powershell Version 3:
Powershell -Command Import-Module (direct)
=== Parent Invocation.MyCommand: [ Import-Module .\moduledetection ]
Powershell -Command Import-Module (dot-source)
=== Parent Invocation.MyCommand: [ Import-Module .\moduleDotSource ]
Powershell -File ...moduledetection.ps1
Powershell Dot-Source

Powershell Version 2:
Powershell -Version 2 -Command Import-Module
=== Parent Invocation.MyCommand: [ Import-Module .\moduledetection ]
Powershell -Version 2 -Command Import-Module (dot-source)
=== Parent Invocation.MyCommand: [ Import-Module .\moduleDotSource ]
Powershell -Version 2 -File ...moduledetection.ps1
Powershell -Version 2 Dot-Source

从显示的输出中我们可以看到(在 Server 2008 R2 上运行),父作用域$MyInvocation.MyCommand包含导入模块语句。我还没有测试它,但我由此推断,如果它是通过链式点源的多重间接,我们可以继续采用父作用域,直到我们得到 Null 或 Import-Module。

现在我们知道如何检测我们是否在一个模块中,并且通过其他资源也可以了解我们是否被点源、直接执行和/或通过模块加载。

于 2013-09-10T20:55:54.063 回答
2

这不会直接回答您的问题,但可能会帮助您实现目标。

使用-Function-Cmdlet参数有import-module选择地导入模块的一部分。

http://technet.microsoft.com/en-us/library/hh849725.aspx

-Function<String[]>

仅将模块中的指定函数导入当前会话。输入功能列表。允许使用通配符。当您导入模块时,某些模块会自动将选定的函数导出到您的会话中。此参数允许您从导出的函数中进行选择。

-Cmdlet<String[]>

仅将模块中的指定 cmdlet 导入当前会话。输入 cmdlet 列表。允许使用通配符。当您导入模块时,某些模块会自动将选定的 cmdlet 导出到您的会话中。此参数允许您从导出的 cmdlet 中进行选择。

于 2013-07-25T20:37:59.617 回答
0

所以我一直在做类似的事情,但我有一个“构建”步骤,它将所有单独的脚本折叠成一个 .psm1 文件以用作模块。

在这种情况下,代码要么在它自己的 .ps1 中运行,要么作为 .psm1 运行,因此您只需要以下内容:

# at the end of some-function-library.ps1
if($MyInvocation.MyCommand.Name.EndsWith('.psm1')){
    Export-ModuleMember -Function:*-SomePrefix*
}

(为什么?我发现单独的脚本更容易管理、调试、版本等...,但 .psm1 显然更容易分发)

于 2015-12-18T04:47:57.253 回答