我有一个 PowerShell 安装程序,我想在执行策略可能受限且需要管理员权限的计算机上执行。
理想情况下,我可以将其包装在一个 cmd 批处理中,如下所示:
powershell -Command "Start-Process powershell -Verb runAs -ArgumentList '-noexit','-ExecutionPolicy','bypass','-File','C:\path\setup.ps1'"
问题是,当
C:\path\setup.ps1
包含空格时,我无法使其工作,而且如果相对(使用cd C:\path
),路径也不起作用。在Windows PowerShell(见PowerShell(核心)7+底部部分),使用
Start-Process -Verb RunAs
启动命令以提升(作为管理员),总是使用C:\Windows\SYSTEM32
作为工作目录 - 即使是 -WorkingDirectory
参数,如果存在,也会被悄悄忽略。-Command
CLI 参数,并且 Set-Location
(cd
) 调用必须先于 指定的脚本调用相对路径。
Start-Process
cmdlet 的 -ArgumentList
参数在概念上可能更可取,但不幸的是,一个 长期存在的错误 使得它 更好地将所有参数编码在一个 单个字符串中 - 见这个答案。
通过
cmd.exe
,Windows PowerShell CLI从
powershell.exe
执行所有这些操作,由于转义要求而使事情复杂化。
powershell.exe
CLI 调用,C:\path 1
和脚本文件setup 1.ps1
:
powershell -Command "Start-Process -Verb RunAs powershell '-NoExit -ExecutionPolicy Bypass -Command cd \\\"C:\path 1\\\"; & \\\".\setup 1.ps1\\\"' "
注意嵌入的
"
字符。是双重转义,如\\\"
:首先作为\"
以便在outerpowershell.exe
调用期间保留,然后再次用于innerone.
虽然这 通常 有效,但存在 edge cases 其中基于
\
的转义是 的,即如果脚本路径或文件名包含 cmd.exe
元字符,例如 &
;在这种情况下,"^""
(原文如此)必须用于第一轮"
-转义:
powershell -Command "Start-Process -Verb RunAs powershell '-NoExit -ExecutionPolicy Bypass -Command "^"" cd \\"^""C:\path 1\\"^""; & \\"^"".\setup 1.ps1\\"^"" "^""'"
注:
从
cmd.exe
(批处理文件),"^""
(原文如此)是将嵌入在整个"
字符串中的"..."
传递给powershell.exe
的最可靠方法,如上所示,而它""
pwsh.exe
,PowerShell(核心)CLI(见下文)。
相比之下,从无壳上下文(例如,计划任务)
\"
在两个版本中都运行良好。
有关详细信息,请参阅此答案。
当您改为调用
pwsh.exe
- PowerShell (Core) 7+ CLI - 可以进行简化,甚至可能不需要解决方法:
pwsh.exe
does 默认情况下保留调用者的工作目录,即使使用 -Verb RunAs
和 does 尊重 -WorkingDirectory
参数与 -Verb RunAs
.
除了
\"
之外,pwsh.exe
更简单支持""
嵌入"
字符。在 "..."
字符串中;后者从 cmd.exe
pwsh.exe
本身现在有一个 -WorkingDirectory
参数,因此允许使用 -File
参数调用脚本:
pwsh.exe -WorkingDirectory "C:\path 1" -Command "Start-Process -Verb RunAs pwsh.exe '-NoExit -ExecutionPolicy Bypass -File ""C:\path 1\setup 1.ps1""'"
这里有一个脚本示例,用于检查进程是否正在提升运行,如果不是,它会尝试启动一个提升的新进程。在这种情况下不需要嵌套文件或使用 CMD。
这显然伴随着 UAC 提示的警告,就像任何其他以提升权限启动的进程一样。
$isAdmin = [System.Security.Principal.WindowsPrincipal]::new(
[System.Security.Principal.WindowsIdentity]::GetCurrent()).
IsInRole('Administrators')
if(-not $isAdmin) {
$params = @{
FilePath = 'powershell' # or pwsh if Core
Verb = 'RunAs'
ArgumentList = @(
'-NoExit'
'-ExecutionPolicy ByPass'
'-File "{0}"' -f $PSCommandPath
)
}
Start-Process @params
return
}
"I'm elevated"
# Code goes here