Reputation: 1624
Suppose I have 2 files, A.ps1
and B.ps1
, in this directory structure:
-- 📁 root
|
|-- A.ps1
|
|-- 📁 subfolder
|
|-- B.ps1
A.ps1:
Write-Host $PSScriptRoot
B.ps1:
. "\root\A.ps1"
Now, taking this fact into account:
Dot sourcing takes the script you've specified and immediately executes it as though it had been at that spot in your original script
B.ps1
, I would expect the result to be \root\subfolder
, but it's \root
, why??If A.ps1
is dot-sourced into B.ps1
, shouldn't the contents of script A.ps1
run as if they were written directly inside of B.ps1
? Meaning, shouldn't $PSScriptRoot
run as if it was called from B.ps1
, and thus evaluate to \root\subfolder
? I've even tested this by wrapping A.ps1
in a function and calling that function in B.ps1
after dot-sourcing. Still yields the same result..
How does dot-sourcing really work?
Upvotes: 4
Views: 1140
Reputation: 27776
As commenters noted, $PSScriptRoot
is independent of scope. From the docs (emphasis mine):
$PSScriptRoot
Contains the full path of the executing script's parent directory.
Dot-Sourcing doesn't literally "include" the script at the spot (contrary to the #include
directive of C++, for instance), PowerShell still knows it is running a different script. The only thing that is different compared to calling the script (explicitly using the call operator &
or running the script by entering its path), is the scoping.
There is a way to get the $PSScriptRoot
of the calling script though. Turn the script to be dot-sourced into an advanced function cmdlet by using the CmdletBinding()
and/or the Parameter()
attribute. This makes the automatic variable $PSCmdlet
available, which gives you (amongst other things), the $PSScriptRoot
of the invoking script.
A.ps1
# CmdletBinding is an attribute of param(), so that is required as well
[CmdletBinding()] param()
$PSScriptRoot # Outputs directory that contains A.ps1
$PSCmdlet.MyInvocation.PSScriptRoot # Outputs directory of the invoking script
Upvotes: 7