查找主PowerShell脚本文件的位置(路径)
我正在寻找获取主(比如“入口点”)PowerShell脚本(*.ps1)文件路径的方法查找主PowerShell脚本文件的位置(路径),powershell,Powershell,我正在寻找获取主(比如“入口点”)PowerShell脚本(*.ps1)文件路径的方法 让我们考虑下面的例子: 我有以下文件: “C:\MyDir1\Main.ps1”调用 “C:\MyDir2\SubDir1\MyModule.psm1”,它调用 “C:\AnotherDir\AnotherScript.ps1” 我运行以下PowerShell命令: PowerShell.exe-文件“C:\MyDir1\Main.ps1” 我想要的:从脚本“AnotherScript.ps1”中,
让我们考虑下面的例子:
- 我有以下文件:
- 我运行以下PowerShell命令:
- 我想要的:从脚本“AnotherScript.ps1”中,我想要找到“C:\MyDir1\Main.ps1”的路径
注意2:变量$global:PSScriptRoot(与$PSScriptRoot不同)是我要查找的,但不幸的是,当从PowerShell会话调用主脚本时,此值为空。这里有一个函数可以完成此任务,但它非常复杂(可能太复杂) 函数Get-EntryPointAbsFilePath(){ #注意1:不要使用“$MyInvocation.PSScriptRoot”,因为它对应于调用脚本的路径(而不是入口点脚本)。 #注2:“$global:PSScriptRoot”与“$PSScriptRoot”不同,似乎对应于入口点脚本目录, #但仅当从powershell命令(如[powershell.exe-File“MainScript.ps1”)调用主脚本时,才设置该值,而不是 #从PowerShell会话(提示符)调用“MainScript.ps1”时,如[PS C:\Temp>.MainScript.ps1]。 $CallStack=获取PSCallStack #我们使用最后一个堆栈元素(对应于第一个调用)。 #当从PowerShell会话(如[PS C:\Temp>.MainScript.ps1])调用主脚本时,此第一次调用的“ScriptName”属性可以为null。 #这是因为PowerShell首先计算输入的命令。 $FirstCall=$CallStack[$CallStack.Count-1]; if($null-ne$FirstCall.ScriptName){ 返回$FirstCall.ScriptName; } #我们接受第二个调用(假设我们在PowerShell会话下运行)。 #要确保此调用来自脚本文件的执行(而不是来自解释器中cmdlet的执行,如模块中的函数), #当从脚本块(如ps1文件)执行调用时,我们检查等于“”的“FunctionName”属性。 #第一次调用不需要此测试,因为无法运行PowerShell模块。 $SecondCall=$CallStack[$CallStack.Count-2]; if($null-ne$SecondCall.ScriptName-和$SecondCall.FunctionName-eq“”){ 返回$SecondCall.ScriptName; } throw“找不到PowerShell入口点脚本。此cmdlet“$($MyInvocation.MyCommand.Name)”仅可通过执行脚本文件来调用。“; }
您可以尝试
@(Get-PSCallStack)[1]。ScriptName
或@(Get-PSCallStack)[1]。位置
我已经完成Get-PSCallStack,这是一种可能性,但我们必须根据入口点的启动方式(从PowerShell会话或从PowerShell.exe文件“Script.ps1”)获取最后一个元素或最后一个元素。在这种情况下,@(Get-PSCallStack)[-1]。ScriptName
会做这个把戏吗?@(Get-PSCallStack)[-1]。ScriptName并不总是做这个把戏,因为当从PowerShell提示符(如[PS C:\Temp>.MainScript.ps1])调用脚本时,“ScriptName”是空的(如[PS C:\Temp>.MainScript.ps1]),那么为什么不做一个类似Get PSCallStack | ForEach对象{的ForEch对象呢
以控制所有可能的用例。我想在超过两个级别时抛出,因为这种情况不应该发生。
function Get-EntryPointAbsFilePath() {
# NOTE 1: Do not use '$MyInvocation.PSScriptRoot' because it corresponds to the path of the calling script (not entry point script).
# NOTE 2: '$global:PSScriptRoot' is not the same as '$PSScriptRoot' and seems to correspond to the entry point script directory,
# but it is set only when the main script is invoked from powershell command like [PowerShell.exe -File "MainScript.ps1"] but not
# when "MainScript.ps1" is invoked from a PowerShell session (prompt) like [PS C:\Temp>. MainScript.ps1].
$CallStack = Get-PSCallStack
# We take the last stack element (correponding to the first call).
# The 'ScriptName' property of this first call can be null when the main script is invoked from a PowerShell session like [PS C:\Temp>. MainScript.ps1].
# This is because PowerShell first evaluates the entered command.
$FirstCall = $CallStack[$CallStack.Count - 1];
if ($null -ne $FirstCall.ScriptName) {
return $FirstCall.ScriptName;
}
# We take the second call (assuming that we are run under a PowerShell session).
# To make sure this call is coming from the execution of a script file (and not from the execution a cmdlet in the interpreter, like a function in a module),
# we check the 'FunctionName' property which equals "<ScriptBlock>" when a call is performed from a script block, like a ps1 file.
# This test is not required for the first call, as a PowerShell module can't be run.
$SecondCall = $CallStack[$CallStack.Count - 2];
if ($null -ne $SecondCall.ScriptName -and $SecondCall.FunctionName -eq "<ScriptBlock>") {
return $SecondCall.ScriptName;
}
throw "No PowerShell entry point script could be found. This cmdlet ""$($MyInvocation.MyCommand.Name)"" is intended to be called only via the execution of a script file.";
}