点源自提升脚本

Dot-sourcing a self-elevate script

我有一个非常冗长的自我提升片段,所以我决定不将其复制到每个需要 运行 作为管理员的脚本的顶部,而是将其移至单独的 .ps1:

function Switch-ToAdmin {
    # Self-elevate the script if required
    if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
        if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) {
            $Cmd = @(
                "-Command Set-Location `"$(Get-Location)`"; & `"$PSCommandPath`""
                "-$($PSBoundParameters.Keys)"
            )
            $ProcArgs = @{
                FilePath     = 'PowerShell.exe'
                Verb         = 'RunAs'
                ArgumentList = $Cmd
            }
            Start-Process @ProcArgs
            Exit
        }
    }
}

因此,对于每个需要提升的脚本,我都会预先添加

. "$PSScriptRoot\self-elevate.ps1"
Switch-ToAdmin
# rest of script

以上操作成功触发了 UAC 提示,但脚本的其余部分不会执行。 这种东西是不允许的吗?

Darin and iRon提供了关键点:

  • Darin 指出 automatic $PSCommandPath variable variable in your Switch-ToAdmin function does not contain the full path of the script from which the function is called, but that of the script in which the function is defined, even if that script's definitions are loaded directly into the scope of your main script via ., the dot-sourcing operator.

    • 这同样适用于自动 $PSScriptRoot 变量,它反映了定义脚本的完整 目录 路径。
  • 此外,更一般地说,函数内部的 automatic $PSBoundParameters variable 反映了 该函数的 绑定参数,而不是其封闭的 script的。

  • iRon 指出 Get-PSCallStack cmdlet 可用于获取有关脚本 的信息来电者,从索引1开始;返回的第一个对象 - 索引 0,当 Get-PSCallStack 输出被捕获到数组中时,表示 current 命令。因此,索引 1 指的是 直接 调用者,从您的 dot-sourced 脚本的角度来看,它是您的主脚本。

因此:

  • 通过自动 $MyInvocation 变量将 $PSCommandPath 替换为 $MyInvocation.PSCommandPath$MyInvocation.PSCommandPath 真实地反映了 调用者的 完整脚本路径,无论调用函数的定义位置如何。

    • 或者,使用 (Get-PSCallStack)[1].ScriptName,尽管 属性 名称暗示了什么,returns 也是调用脚本的 完整路径
  • $PSBoundParameters替换为
    (Get-PSCallStack)[1].InvocationInfo.BoundParameters

    • 请注意,还有 (Get-PSCallStack)[1].Arguments,但它似乎只包含一个 字符串 ,包含仅 semi-structured 和因此不允许对单个参数进行稳健的重建。

顺便说一句:

即使 $PSBoundParameters 包含预期信息,如果您的脚本仅定义 一个 参数,"-$($PSBoundParameters.Keys)" 也只会成功传递绑定参数,如果该参数是一个 [switch] 参数,如果它实际上在每次调用中传递。

在这种情况下,通过 稳健地 传递参数很难做到,并且具有固有的局限性 - 请参阅 以了解 - 复杂的 - 使其工作的尝试尽可能。