使用 PowerShell.exe 的 Start-Process 表现出嵌入单引号和双引号的不同行为

Start-Process with PowerShell.exe exhibits different behavior with embedded single quotes and double quotes

First, in case anyone wonders why we're invoking PowerShell in this way, I ran into this behavior with a more complex command we were building, but the behavior can be exhibited using a more simple example as shown below. In practice, we are running a command under 32-bit PowerShell as admin with additional variables rendered in the string (hence why I don't simply use single-quotes for the outer portion), but that doesn't seem to factor into the behavior below.


当我通过 Start-Process 调用 PowerShell 时,如果我在 PowerShell 可执行文件的 -Command 参数周围使用单引号,我会得到一些奇怪的行为。例如:

Start-Process -FilePath Powershell.exe -ArgumentList "-Command 'ping google.com'"

只是将 ping google.com 渲染为输出并退出。但是,如果我使用嵌套双引号而不是单引号,如下所示:

Start-Process -FilePath Powershell.exe -ArgumentList "-Command `"ping google.com`""

ping 运行并产生预期的输出:

Pinging google.com [173.194.78.113] with 32 bytes of data:

Reply from 173.194.78.113: bytes=32 time=34ms TTL=45

Reply from 173.194.78.113: bytes=32 time=33ms TTL=45

Reply from 173.194.78.113: bytes=32 time=35ms TTL=45

Reply from 173.194.78.113: bytes=32 time=32ms TTL=45

Ping statistics for 173.194.78.113:

Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),

Approximate round trip times in milli-seconds:

Minimum = 32ms, Maximum = 35ms, Average = 33ms

如果我对 -Command 参数使用单引号而不是双引号,为什么命令字符串只是按原样呈现而不是执行?

powershell.exe 的帮助没有提到支持单引号...您为什么要使用它们?

-Command
    Executes the specified commands (and any parameters) as though they were
    typed at the Windows PowerShell command prompt, and then exits, unless
    NoExit is specified. The value of Command can be "-", a string. or a
    script block.

    If the value of Command is "-", the command text is read from standard
    input.

    If the value of Command is a script block, the script block must be enclosed
    in braces ({}). You can specify a script block only when running PowerShell.exe
    in Windows PowerShell. The results of the script block are returned to the
    parent shell as deserialized XML objects, not live objects.

    If the value of Command is a string, Command must be the last parameter
    in the command , because any characters typed after the command are
    interpreted as the command arguments.

    To write a string that runs a Windows PowerShell command, use the format:
        "& {<command>}"
    where the quotation marks indicate a string and the invoke operator (&)
    causes the command to be executed.

我们可以从这里删除启动进程。嵌入的双引号和单引号似乎被 powershell 可执行文件区别对待。启动过程与此行为无关。这是 powershell 在命令行上的行为方式。我看不出有什么办法可以改变它。您也可以执行 "start-job -runas32",但它不会被提升。另一个选项是 powershell 的“-file”选项而不是“-command”。这些示例 运行 来自 Osx (unix) powershell core:

pwsh -c "'ping -c 1 google.com'"

ping -c 1 google.com


pwsh -c "`"ping -c 1 google.com`""

PING google.com (172.217.10.110): 56 data bytes
64 bytes from 172.217.10.110: icmp_seq=0 ttl=52 time=20.020 ms

--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 20.020/20.020/20.020/0.000 ms


pwsh -c '"ping -c 1 google.com"'        
PING google.com (172.217.6.206): 56 data bytes
64 bytes from 172.217.6.206: icmp_seq=0 ttl=52 time=22.786 ms

--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 22.786/22.786/22.786/0.000 ms

is correct in that the use of Start-Process is incidental to your question and that the behavior is specific to PowerShell's CLI (powershell.exe for Windows PowerShell, and pwsh PowerShell [核心] 6+):

Windows[1]上有两个层评估考虑:

  • (a) 命令行初始解析为参数。

  • (b) 评估(space-串联)结果参数作为PowerShell代码,由于使用 -Command (-c) CLI 参数。

回复(一):

与 Windows 上的大多数控制台程序一样,PowerShell 只能识别 " 个字符。 (双引号)- 不是 ' (单引号)- 具有 syntactic 功能。[2]

  • 也就是说,除非 " 个字符。 转义,它们是字符串定界符,它们本身在解析过程中被删除

  • 如上所示,' 个字符。 没有删除。

无论结果是什么参数 - 一个可能 "- 剥离标记的数组 - 连接 单个 space 在它们之间,成为 (b) 的输入。


为了在您的示例的上下文中解释这一点,Start-Process 从图片中取出:

注意:以下适用于从cmd.exeshell涉及[140=](包括Start-Process和Windows运行(WinKey-R)对话框)。相比之下,如果需要,PowerShell 重新引用 幕后命令行始终使用 "
换句话说:以下适用于 PowerShell.

所见的命令行

-引号命令:

# Note: This *would* work for calling ping if run from 
#       (a) PowerShell itself or (b) from a POSIX-like shell such as Bash.
#       However, via cmd.exe or any context where *no* shell is involved,
#       notably Start-Process and the Windows Run dialog, it does not.
powershell -Command 'ping google.com'
  • (a) 导致 PowerShell 找到以下两个逐字参数:'pinggoogle.com'

  • (b) 将这些逐字参数连接成 'ping google.com'[2] 并执行 作为 PowerShell 代码,因此输出这个字符串文字的内容ping google.com

双引号-引号命令:

powershell -Command "ping google.com"
  • (a) 导致 PowerShell 剥离 syntactic " 字符,找到以下单个逐字参数:ping google.com

  • (b) 然后导致此逐字参数 - ping google.com - 作为 PowerShell 代码执行,因此导致 命令调用 ,即带有参数 google.com.

  • ping 可执行文件

[1] 在类 Unix 平台上,第一层不适用,因为被调用的程序只会看到 array of verbatim 参数,而不是他们自己必须解析为参数的命令行。并不是说如果你从类似 POSIX 的 shell(例如类 Unix 平台上的 bash)调用 PowerShell CLI,那么 即 shell 将单引号识别为字符串定界符,并在 PowerShell 看到它们之前将它们剥离。

[2] 令人惊讶的是,在 Windows 上,最终由 每个单独的程序 来解释命令行,而一些 do 选择也将 ' 识别为字符串定界符(例如,Ruby)。然而,Windows 上的许多程序——包括 PowerShell 本身——都是基于 C 运行时的,它只识别 ".

[3] 顺便说一句,请注意这意味着 whitespace 规范化 正在发生:也就是说,
powershell -Command 'ping google.com' 同样会导致 'ping google.com'.