我试图在启动提升的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会话在打开后立即关闭。我认为开始-过程行是错误的。
编辑:
我试着用一个函数 Package 脚本块内容,并稍微改变了启动进程,现在它工作了:
$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条答案
按热度按时间ljo96ir51#
[1]还要注意zdan有用的调试技巧:在PowerShell CLI调用中临时将
-NoExit
添加到-Command
之前,以保持会话打开,从而允许您检查错误消息。问题不是使用了 multiline 命令字符串,而是与 quoting有关:
'...'
-在-Command
参数中引用(除非这些'
字符是要执行的PowerShell代码的一部分)-当从外部调用 * 时-使用Start-Process
就是一个例子- PowerShell将其 * 进程命令行 * 上的'...'
字符串视为 * 逐字字符串 *。cmd.exe
或从计划任务调用时),当调用其CLI(powershell.exe
用于Windows PowerShell,pwsh
用于PowerShell(Core)7+)时,只有"..."
引用具有 * 语法 * 功能(在进程命令行上)。"
字符,这些字符将成为PowerShell命令的一部分,以\"
的形式执行-否则PowerShell的初始 CLI 解析将删除它们。让你的代码基于这些要求 * 健壮地工作*有点麻烦(这里使用了一个字符串来引用方便和可读性):
-replace
操作确保任何"
字符。被转义为\"
,以保护它们免受初始CLI解析。(\\*)
捕获值中紧接在"
之前的任何\
字符,这些字符必须 * 加倍 * 才能保留,这就是替换表达式中的$1$1
所做的。$content
和$path
的情况下,由于使用了嵌入式'...'
引用,因此任何嵌入式'
字符。必须另外转义为''
(因为Windows上的文件路径不能包含"
字符,所以$paths
值只需要 * 这个转义。'...'
引号而不是"..."
确保$content
和$replace
的值不会在目标PowerShell示例中进行 * 另一轮 * 插值。重要:
$scriptBlock
、$content
和$path
中的 * 任何值 * 而设计的- * 在特定情况下 * 您可以 * 不执行-replace
操作 *,但您需要完全理解规则以了解何时执行-因此,下一节中介绍的Base64解决方案更可取。-Command
的PowerShell代码封装在"..."
中并不是严格需要的,但是 * 不 * 这样做会产生 * 空白规范化 * 的副作用(通常 * 不会 * 是一个问题,但可能是);例如,如果$content
包含Four spaces: ...
,它将变成Four spaces: ...
。也就是说,任何 * 两个或多个空格 * 的运行都将变成 * 单个 * 空格。"..."
shell ,作为要执行的PowerShell代码的一部分的"
字符 * 必须 * 转义为\"
'...'
shell 避免了 * 原始 * 尝试的主要问题,但它仍然会删除未转义的"
字符,这解释了您尝试将-Object
添加到Write-Host
调用的问题:"
字符,Write-Host -Object "PATH: $filePath
nn"
变成了Write-Host -Object PATH: $filePath
nn
,这导致了一个 syntax 错误,因为使用 named 参数绑定-Object
只接受一个 * 单个 * 参数(然而,它可能是一个,
分隔的 * 数组 * 值),但是"
删除将单个参数变成了 * 多个 * 参数。(仅当您使用 * 位置 * 参数绑定时,即 * 如果没有 *-Object
,Write-Host
是否接受 * 多个单独的 * 参数,这就是为什么-尽管删除了"
-它仍然 * 碰巧工作 *。$content
和$path
值包含'
字符,代码也会中断。如果你想*避免 * 引号和
-replace
操作,你可以Base64编码你的脚本块和参数,并使用结果与**-EncodedCommand
和(目前没有文档)-EncodedArguments
**CLI参数:注意事项:
foo
和c:\bar
传递给脚本块的参数,这些值的 order 意味着-fileContent
和-filePath
作为目标参数,但您不能使用-fileContent foo
和-filePath bar
,即不能使用 named 参数。