我有一个self - elevate代码片段,它相当冗长,所以我决定不把它复制到需要以admin身份运行的每个脚本的顶部,而是将其移动到一个单独的。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
}
}
}
所以对于每个需要提升的脚本我都要加上
. "$PSScriptRootself-elevate.ps1"
Switch-ToAdmin
# rest of script
以上操作成功地处理了UAC提示,但脚本的其余部分不会执行。这类事情是不允许的吗?
Darin和iRon提供了关键的指针:
-
Darin指出(
Switch-ToAdmin
函数中的自动$PSCommandPath
变量变量)不包含调用函数的脚本的完整路径,而是包含定义函数的脚本的完整路径,即使该脚本的定义通过.
(点源操作符)直接加载到主脚本的作用域。- 同样适用于自动
$PSScriptRoot
变量,它反映了定义脚本的完整目录路径。
- 同样适用于自动
-
而且,更一般地说,函数内部的自动
$PSBoundParameters
变量反映了函数的绑定参数,而不是其封闭的脚本。 -
iRon指出
Get-PSCallStack
cmdlet可用于获取有关脚本调用者的信息,从索引1
开始;当在数组中捕获Get-PSCallStack
输出时,返回的第一个对象- index0
表示当前命令。因此,索引1
引用直接调用者,从点源脚本的角度来看,它是主脚本。 因此:
-
将
$PSCommandPath
替换为$MyInvocation.PSCommandPath
,通过自动的$MyInvocation
变量。$MyInvocation.PSCommandPath
真正反映了调用者的完整的脚本路径,而不管被调用的函数是在哪里定义的。- 或者,使用
(Get-PSCallStack)[1].ScriptName
,不管属性名如何,它也会返回调用脚本的完整路径。
- 或者,使用
-
用
(Get-PSCallStack)[1].InvocationInfo.BoundParameters
代替$PSBoundParameters
- 请注意,也有
(Get-PSCallStack)[1].Arguments
,但它似乎只包含一个字符串,包含所有参数的表示,只是半结构化的,因此不允许单个参数的鲁棒重建。
- 请注意,也有
作为题外话:
即使$PSBoundParameters
包含预期的信息,"-$($PSBoundParameters.Keys)"
也只有在脚本只定义一个参数,如果该参数是[switch]
参数,并且在每次调用中都实际传递该参数时,才能成功传递绑定的参数。
在这种情况下,通过健壮地传递参数是很难做到的,并且具有固有的局限性-请参阅下面的复杂尝试,以使其尽可能好地工作。