问题
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.
When I invoke PowerShell through Start-Process
, I get some odd behaviors if I use single quotes surrounding the -Command
parameter to the PowerShell executable. For example:
Start-Process -FilePath Powershell.exe -ArgumentList "-Command 'ping google.com'"
just renders ping google.com
as the output and exits. However, if I use nested double-quotes instead of the single-quotes as follows:
Start-Process -FilePath Powershell.exe -ArgumentList "-Command `"ping google.com`""
ping
runs and produces the expected output:
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
Why does the command string simply render as-is instead of execute if I use the single-quotes for the -Command
parameter instead of double-quotes?
回答1:
js2010's helpful answer 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 for PowerShell [Core] 6+):
On Windows[1], there are two layers of evaluation to consider:
(a) The initial parsing of the command line into arguments.
(b) The evaluation of the (space-concatenated) resulting arguments as PowerShell code, due to use of the
-Command
(-c
) CLI parameter.
Re (a):
As most console programs on Windows do, PowerShell recognizes only "
chars. (double quotes) - not also '
(single quotes) - as having syntactic function.[2]
That is, unless
"
chars. are escaped, they are string delimiters that themselves are removed during parsing.As implied by the above,
'
chars. are not removed.
Whatever arguments are the result - an array of possibly "
-stripped tokens - are concatenated with a single space between them, which becomes the input for (b).
To explain this in the context of your example, with Start-Process
taken out of the picture:
Note: The following applies to calling from cmd.exe
or any context where no shell is involved (including Start-Process
and the Windows Run (WinKey-R) dialog). By contrast, PowerShell re-quotes the command line behind the scenes to always use "
, if needed.
To put it differently: the following applies to command lines as seen by PowerShell.
Single-quoted command:
# 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) results in PowerShell finding the following two verbatim arguments:
'ping
andgoogle.com'
(b) concatenates these verbatim arguments to form
'ping google.com'
[2] and executes that as PowerShell code, therefore outputs the content of this string literal,ping google.com
Double-quoted command:
powershell -Command "ping google.com"
(a) results in PowerShell stripping the syntactic
"
characters, finding the following, single verbatim argument:ping google.com
(b) then results in this verbatim argument -
ping google.com
- being executed as PowerShell code, which therefore results in a command invocation, namely of theping
executable with argumentgoogle.com
.
[1] On Unix-like platforms, the first layer doesn't apply, because programs being invoked only ever see an array of verbatim arguments, not a command line that they themselves must parse into arguments. Not that if you call the PowerShell CLI from a POSIX-like shell such as bash
on Unix-like platforms, it is that shell that recognizes single-quotes as string delimiters, and strips them before PowerShell sees them.
[2] Surprisingly, on Windows it is ultimately up to each individual program to interpret the command line, and some do choose to also recognize '
as string delimiters (e.g., Ruby). However, many programs on Windows - including PowerShell itself - are based on the C runtime, which only recognizes "
.
[3] As an aside, note that this implies that whitespace normalization is taking place: that is,powershell -Command 'ping google.com'
would equally result in 'ping google.com'
.
回答2:
We can drop start-process from this. It seems true that embedded double and single quotes are treated differently by the powershell executable. Start-process is not relevant to this behavior. This is the way powershell behaves on the command line. I don't see a way to change it. You can also do "start-job -runas32", but it wouldn't be elevated. Another option is powershell's "-file" option instead of "-command". These examples are run from within 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
回答3:
The help for powershell.exe mentions nothing about supporting single-quotes... why would you be using them?
-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.
来源:https://stackoverflow.com/questions/61109118/start-process-with-powershell-exe-exhibits-different-behavior-with-embedded-sing