How to detect whether a script is dot-sourced, or loaded as part of a module using powershell v2+?
powershell, powershell-2.0, powershell-3.0
Solution
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.
Problem
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`?