PowerShell使用Start-Process在脚本块中执行一个函数会对双引号产生奇怪的影响 [英] PowerShell Executing a function within a Script Block using Start-Process does weird things with double quotes

查看:265
本文介绍了PowerShell使用Start-Process在脚本块中执行一个函数会对双引号产生奇怪的影响的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我已经创建了一个简化的示例PowerShell脚本来重现问题。以下是代码片段:

  $ ScriptBlock = {
函数测试
{
$ status = 这是一个字符串
写入输出$状态

}
启动进程-FilePath PowerShell -ArgumentList-NoExit -NoProfile -ExecutionPolicy Bypass -Command& { $ ScriptBlock Test}

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


这样:术语'This'不被识别为cmdlet,
函数,脚本文件或可操作程序。检查
名称的拼写,或者如果包含路径,请检查路径是否正确,并且
再试一次。


所以它试图将字符串视为一个commad,就好像我刚刚在脚本的新行中键入这是一个字符串本身。 / p>

如果我改变行

  $ status =这是一个字符串

  $ status ='这是一个字符串'

该脚本按预期工作,只输出字符串这是一个字符串



另外一个我注意到的奇怪问题是如果我不' t使用一个变量,并使用:

  Write-Output这是一个字符串

lockquote



$ b





a

string

但如果我使用如下单引号:

pre $ Write-输出'这是一个字符串'

然后它按照预期在一行上输出整个句子。 p>

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

Answer



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



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

  $ status =这是一个字符串



$ p $ $ status =这是一个字符串

或者这个:

  $ status ='这是一个字符串''

或此:

  $ status = {这是一个字符串} 

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

  $ status =这是一个评估$ someVariable的字符串

还是不确定这是一个Bug还是By Design,但至少我们有一个解决方法,因为这解决了我上面描述的两个问题。

解决方案

如果我将脚本更改为是

   - 命令$ ScriptBlock 

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

  gci function:test | fl 

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

  $ status =这是一个字符串

在单引号版本上进行相同的测试显示:

  $ status ='这是一个字符串'

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

  $ status =这是一个字符串

他们通过OK。同样,即使scriptblocks是编译代码,如果将它们展开为字符串,它在我看来就像是嵌入为文本:

  > $ s = {hello} 
> --- $ s ---
---hello---



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



PowerShell将脚本块作为字符串扩展到您的命令中,然后这些字符串中的单引号将被重新解释为引用参数,并在调用中被删除。这是一个已知的问题,或者一个错误或者是设计,这取决于你阅读链接连接文章的方式。


I have a PowerShell script that edits the registry, so it needs to run as admin. To do this, I start up a new PowerShell process from my running PowerShell script, and pass in part of the registry key path using a Script Block with a function in it. When I use double quotes within that function, PowerShell tries to interpret them as commands, rather than a string. If I use single quotes though, then everything works fine.

I've created a little stripped down sample powershell script that reproduces the problem. Here's the snippet:

$ScriptBlock = {
    function Test
    {
        $status = "This is a string"
        Write-Output $status
    }
}
Start-Process -FilePath PowerShell -ArgumentList "-NoExit -NoProfile -ExecutionPolicy Bypass -Command & {$ScriptBlock Test}"

So in the new PowerShell process it will first define the code in the script block and then call the Test method, and it produces this error:

This : The term 'This' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

So it's trying to treat the string as a commad, as if I had just typed This is a string by itself on a new line in my script.

If I change the line

$status = "This is a string"

to

$status = 'This is a string'

the script works as expected and simply outputs the string This is a string.

Another strange problem I've noticed is that if I don't use a variable and just use:

Write-Output "This is a string"

then it outputs each word on a separate line like this:

This

is

a

string

but if I use single quotes like this:

Write-Output 'This is a string'

then it outputs the entire sentence on one line as expected.

Does anybody know why PowerShell is behaving strangely in these situations?

Answer

So as TessellatingHeckler mentions, the solution is do wrap anything that is double quoted in double double quotes, single quotes, or you can use brackets.

So in my example, you would change:

$status = "This is a string"

to this:

$status = """This is a string"""

or this:

$status = '"This is a string"'

or this:

$status = {"This is a string"}

If you want to evaluate a variable in your string though (i.e. see the variable's value), then you have to go with the double double quotes method:

$status = """This is a string that evaluates $someVariable"""

Still not sure if this is a Bug or By Design, but at least we have a workaround, as this fixes both of the problems I described above.

解决方案

If I change your script to be

-Command $ScriptBlock

Run it, and have it open a new shell window, then run

gci function:test | fl 

to see the function definition in the new window, the code shown is

$status = This is a string

with the same test on the single quote version it shows

$status = 'This is a string'

So it's losing the double quotes. Escape them with double quotes

$status = """This is a string"""

and they come through OK. Also even though scriptblocks are compiled code, it looks to me like they are embedded as text if you expand them into a string:

> $s = { "hello" }
> "---$s---"
---"hello"---

So I think you're hitting this kind of quoting problem: PowerShell stripping double quotes from command line arguments and the answer by Droj particularly, saying "The weird thing about sending parameters to external programs is that there is additional level of quote evaluation. I don't know if this is a bug, but I'm guessing it won't be changed, because the behavior is the same when you use Start-Process and pass in arguments.".

PowerShell is expanding the script block as a string into your command, then those single quotes around the string are being reinterpreted as quoted parameters and removed somewhere in the invoking. Which is either a known problem, or a bug, or by-design, depending on how you read that linked connect article.

这篇关于PowerShell使用Start-Process在脚本块中执行一个函数会对双引号产生奇怪的影响的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆