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
?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With