How to detect whether a script is dot-sourced, or loaded as part of a module using powershell v2+?

*爱你&永不变心* 提交于 2019-12-11 01:19:31

问题


Given a ps1 file as part of a module with the following code:

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

When loaded as part of the module, everything is fine. If I dot-source the script, I get

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

I know I could just add a -ErrorAction:Ignore on the Export-ModuleMember, but that's not the point. I'd like to have a script run differently whether it has been imported, or dot-sourced.

In version 2, one could probably write a hack around the $PSScriptRoot, but that is just a hack, and doesn't work in version 3 where they "fixed" $PSScriptRoot to never be null. I've tried looking at various items in $MyInvocation, but either I've missed something, or it has nothing useful.

I've also tried to run Get-Variable inside and outside a module, but again found no differences.

What have I missed that is different when running as Import-Module vs . myscript.ps1?


回答1:


This doesn't directly answer your question but may help you achieve your goal.

Use the -Function or -Cmdlet parameters of import-module to selectively import parts of the module.

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

-Function<String[]>

Imports only the specified functions from the module into the current session. Enter a list of functions. Wildcard characters are permitted. Some modules automatically export selected functions into your session when you import the module. This parameter lets you select from among the exported functions.

-Cmdlet<String[]>

Imports only the specified cmdlets from the module into the current session. Enter a list of cmdlets. Wildcard characters are permitted. Some modules automatically export selected cmdlets into your session when you import the module. This parameter lets you select from among the exported cmdlets.




回答2:


It appears that I found the answer to this. I was missing something in $MyInvocation, because I was looking in the wrong scope. Given the following files:

# .\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 }

and finally, make a symlink from the original ps1 to a psm1 with the correct name to load as a direct module.

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

The output shows that the parent scope has the key.

Output:

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

As we can see from the output shown (run on Server 2008 R2), the parent scope's $MyInvocation.MyCommand contains the import module statement. I have not tested it yet, but I infer from this that if it's multiple indirection through chained dot-sourcing, we can keep taking the parent scope until we get either Null or an Import-Module.

Now we know how to detect if we're in a module or not, and through other resources can also grok if we're being dot-sourced, executed directly, and/or loaded through a module.




回答3:


So I've been doing something similar, but I have a 'build' step that collapses all the separate scripts into a .psm1 file for use as a module.

In that case the code either runs in it's own .ps1, or runs as a .psm1, so you simply need the following:

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

(Why? I find the seperate scripts much easier to manage, debug, version etc..., but the .psm1 is obviously easier to distribute)



来源:https://stackoverflow.com/questions/17867953/how-to-detect-whether-a-script-is-dot-sourced-or-loaded-as-part-of-a-module-usi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!