PowerShell使用Start-Process在脚本块中执行函数会使用双引号进行奇怪的操作

dea*_*dog 6 string powershell function start-process scriptblock

我有一个编辑注册表的PowerShell脚本,所以它需要以管理员身份运行.为此,我从运行的PowerShell脚本启动一个新的PowerShell进程,并使用带有函数的脚本块传递部分注册表键路径.当我在该函数中使用双引号时,PowerShell会尝试将它们解释为命令,而不是字符串.如果我使用单引号,那么一切正常.

我创建了一个简单的样本PowerShell脚本来重现问题.这是片段:

$ScriptBlock = {
    function Test
    {
        $status = "This is a string"
        Write-Output $status
    }
}
Start-Process -FilePath PowerShell -ArgumentList "-NoExit -NoProfile -ExecutionPolicy Bypass -Command & {$ScriptBlock Test}"
Run Code Online (Sandbox Code Playgroud)

因此,在新的PowerShell流程中,它将首先在脚本块中定义代码,然后调用Test方法,并产生以下错误:

这:术语"This"不被识别为cmdlet,函数,脚本文件或可操作程序的名称.检查名称的拼写,或者如果包含路径,请验证路径是否正确,然后重试.

所以它试图将字符串视为一个commad,就好像我刚刚This is a string在我的脚本中的新行上键入它一样.

如果我换行

$status = "This is a string"
Run Code Online (Sandbox Code Playgroud)

$status = 'This is a string'
Run Code Online (Sandbox Code Playgroud)

该脚本按预期工作,只是输出字符串This is a string.

我注意到的另一个奇怪的问题是,如果我不使用变量,只需使用:

Write-Output "This is a string"
Run Code Online (Sandbox Code Playgroud)

然后它在一个单独的行上输出每个单词,如下所示:

这个

一个

但如果我使用这样的单引号:

Write-Output 'This is a string'
Run Code Online (Sandbox Code Playgroud)

然后它按预期在一行输出整个句子.

有人知道为什么PowerShell在这些情况下会表现得很奇怪吗?

回答

正如TessellatingHeckler所提到的,解决方案是用双引号,单引号包装任何双引号,或者你可以使用括号.

所以在我的例子中,你会改变:

$status = "This is a string"
Run Code Online (Sandbox Code Playgroud)

对此:

$status = """This is a string"""
Run Code Online (Sandbox Code Playgroud)

或这个:

$status = '"This is a string"'
Run Code Online (Sandbox Code Playgroud)

或这个:

$status = {"This is a string"}
Run Code Online (Sandbox Code Playgroud)

如果你想在你的字符串中评估一个变量(即看到变量的值),那么你必须使用双重双引号方法:

$status = """This is a string that evaluates $someVariable"""
Run Code Online (Sandbox Code Playgroud)

仍然不确定这是一个Bug还是设计,但至少我们有一个解决方法,因为这解决了我上面描述的两个问题.

Tes*_*ler 4

如果我将你的脚本更改为

-Command $ScriptBlock
Run Code Online (Sandbox Code Playgroud)

运行它,让它打开一个新的 shell 窗口,然后运行

gci function:test | fl 
Run Code Online (Sandbox Code Playgroud)

在新窗口中查看函数定义,显示的代码是

$status = This is a string
Run Code Online (Sandbox Code Playgroud)

对它显示的单引号版本进行相同的测试

$status = 'This is a string'
Run Code Online (Sandbox Code Playgroud)

所以它失去了双引号。用双引号转义它们

$status = """This is a string"""
Run Code Online (Sandbox Code Playgroud)

他们都顺利通过了。另外,即使脚本块是编译后的代码,在我看来,如果将它们扩展为字符串,它们就会作为文本嵌入:

> $s = { "hello" }
> "---$s---"
---"hello"---
Run Code Online (Sandbox Code Playgroud)

所以我认为你遇到了这种引用问题:PowerShell 从命令行参数中删除双引号,特别是Droj的回答,说“向外部程序发送参数的奇怪之处在于存在额外的引用评估级别。我不知道这是否是一个错误,但我猜它不会改变,因为当您使用 Start-Process 并传入参数时,行为是相同的。”。

PowerShell 将脚本块作为字符串扩展到命令中,然后字符串周围的单引号将被重新解释为带引号的参数,并在调用中的某处删除。这要么是已知问题,要么是错误,要么是设计使然,具体取决于您如何阅读该链接的连接文章。