我试图在启动提升的 powershell 会话时将脚本块作为命令传递以运行,但我找不到使其工作的方法。我认为这是因为文件内容是多行的?
$content = @"
This is a
multiline
file content.
"@
$path = 'C:\test.txt'
$scriptBlock = {
param($fileContent, $filePath)
Write-Host "PATH: $filePath`n`n"
Write-Host "CONTENT:`n$fileContent"
[System.IO.File]::WriteAllText($filePath, $fileContent, [System.Text.Encoding]::GetEncoding('Windows-1252'))
Pause
}
Start-Process -FilePath 'PowerShell' -Verb 'RunAs' -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command '&{$scriptBlock} `"$content`" `"$path`"'" -Wait
Pause
我错过了什么?有没有更好的方法来实现这一目标?
我尝试了上面的代码,但提升的 Powershell 会话在打开后立即关闭。我认为 Start-Process 行是错误的。
编辑:
我尝试用函数包装脚本块内容并稍微更改 Start-Process,现在它可以工作了:
$content = @"
This is a
multiline
file content.
"@
$path = 'C:\test.txt'
$scriptBlock = {
function wrappingFunc {
param(
$filePath,
$fileContent
)
Write-Host "PATH: $filePath"
Write-Host "CONTENT: $fileContent"
[System.IO.File]::WriteAllText($filePath, $fileContent, [System.Text.Encoding]::GetEncoding('Windows-1252'))
Pause
}
}
Start-Process -FilePath 'PowerShell' -Verb 'RunAs' -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command & {$scriptBlock wrappingFunc -filePath '$path' -fileContent '$content'}" -Wait
Pause
我实际上并不完全理解为什么它会这样工作。有人可以澄清吗?
我还注意到一个非常奇怪的行为。如果我更改脚本块中的以下行:
Write-Host "PATH: $filePath"
Write-Host "CONTENT: $fileContent"
至:
Write-Host -Object "PATH: $filePath"
Write-Host -Object "CONTENT: $fileContent"
它停止工作。为什么简单地添加
-Object
会破坏它?
[1] 另请注意 zdan 的有用调试提示:在 PowerShell CLI 调用中的
-NoExit
之前临时添加 -Command
以保持会话打开,这样您就可以检查错误消息。
问题不在于使用multiline命令字符串,它与quoting有关:
不要在
'...'
参数中使用 -Command
引号(除非那些 '
字符是要执行的 PowerShell 代码的一部分) - 当从外部调用 时 - 使用 Start-Process
是一个示例 - PowerShell 将其 process 命令行上的
'...'
字符串视为 verbatim string.
cmd.exe
调用或从计划任务调用时)只有 powershell.exe
引用具有syntactic 功能(在进程命令行上)。
Escape
pwsh
字符将成为 PowerShell 命令的一部分以执行 "..."
根据这些要求让你的代码健壮地工作有点麻烦(一个
here-string用于引用方便和可读性):
"
\"
操作确保任何
# ...
Start-Process -FilePath PowerShell -Verb RunAs -Wait -ArgumentList @"
-NoProfile -ExecutionPolicy Bypass -Command "
& {
$($scriptBlock -replace '(\\*)"', '$1$1\"')
} \"$($content -replace '(\\*)"', '$1$1`\"')\" \"$path\"
"
"@
字符。作为引用的变量值的一部分被转义为
-replace
正则表达式中的
"
捕获值中紧接在
\"
之前的任何
(\\*)
字符,这些字符必须被 \
所做的。
在 "
的情况下,它们必须additionally
$1$1
$content
字符串之间(对于 `
没有必要,因为文件系统路径不能包含 "..."
字符。在 Windows 上)。
如果您不想单独处理
$path
操作,则必须对脚本块(包括其参数)进行 Base64 编码,并将其与
"
CLI 参数一起使用:-replace
请注意,
one- 尽管更简单 -
-EncodedCommand
操作仍然是必要的,即将嵌入 # ...
# Create a Base64 representation of the bytes that make up the
# "Unicode" (UTF-16LE) encoding of the command string, for use
# with the -EncodedCommand PowerShell CLI parameter.
$encodedCommand = [Convert]::ToBase64String(
[Text.Encoding]::Unicode.GetBytes(@"
& {
$scriptBlock
} "$($content -replace '"', '`"')" "$path"
"@
)
)
# Note the use of -EncodedCommand
Start-Process -FilePath 'PowerShell' -Verb 'RunAs' -Wait -ArgumentList @"
-NoProfile -ExecutionPolicy Bypass -EncodedCommand $encodedCommand
"@
中的任何
-replace
转义为 "
.