It seems to me that how the Windows cmd.exe
parses argument string is different from how normal a C-compiled exe does it.
To illustrate, cmd /C "echo ok"
prints "ok" correctly. But, cmd "/C" "echo ok"
results in
'"echo ok' is not recognized as an internal or external command,
operable program or batch file.
For comparison, here is a C program, "CommandArguments.c", that prints arguments line-by-line:
int main(int argc, char *argv[])
{
int i;
for (i = 0; i < argc; ++i) {
printf("%s\n", argv[i]);
}
}
If I run CommandArguments.exe "/C" "echo ok"
, it correctly prints
CommandArguments.exe
/C
echo ok
I'm asking this because I'm implementing an API to wrap CreateProcess
. I quotes and escape all input arguments before passing to CreateProcess
. It works for most things but not the cmd
due to the above issue.
So, I want to know why the cmd
behaves differently? What is its argument parsing rule? Is there any other program also parse arguments in different ways?
cmd.exe interacts with the user through a command-line interface. On Windows, this interface is implemented through the Win32 console. cmd.exe may take advantage of features available to native programs of its own platform.
Open a command prompt (Windows+R, type "cmd" and hit enter). Then change to the directory housing your executable ("cd enter-your-directory-here"), and run the command with the parameters.
Command line arguments are just values that are passed to an executable file when it is run. Also known as "command line switches" or "command line options," command line arguments are usually used to set program options, or to pass along the location of a file that the program should load.
You can test command line arguments by running an executable from the "Command Prompt" in Windows or from the "DOS prompt" in older versions of Windows. You can also use command line arguments in program shortcuts, or when running an application by using Start -> Run. This will start notepad with a blank document.
Any application can parse the command line in any way it sees fit. Most applications use the C runtime library parser, but there is no requirement to do so.
Ideally, your API should require that the caller provide a single string for the command line rather than an array of arguments, since that is the correct syntax for starting a Windows process.
If that isn't feasible, you should at a minimum provide an option for the caller to do so, in the event that the target application requires special treatment.
As for the command processor, its parsing behaviour is documented in the built-in help (cmd /?
):
If /C or /K is specified, then the remainder of the command line after the switch is processed as a command line, where the following logic is used to process quote (") characters:
If all of the following conditions are met, then quote characters on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters, where special is one of: &<>()@^|
- there are one or more whitespace characters between the two quote characters
- the string between the two quote characters is the name of an executable file.
Otherwise, old behavior is to see if the first character is a quote character and if so, strip the leading character and remove the last quote character on the command line, preserving any text after the last quote character.
That's a bit of a mess, but you can simplify it by providing the /S switch. If the command you want to run is [foo]
then simply use
cmd /s /c "[foo]"
Interresting observation! cmd.exe
really works differently! Looking into the documentation of cmd.exe /?
reveals the following:
If you specify /c or /k, cmd processes the remainder of string and quotation marks are preserved
Experimenting with that I found that the /C
option throws normal command line processing out of the window!
C:\Users\Lukas>cmd "/C" "ECHO Hallo"
'"echo ok' is not recognized as an internal or external command,
operable program or batch file.
C:\Users\Lukas>cmd /CECHO Hallo
Hallo
C:\Users\Lukas>cmd "/CECHO Hallo"
Hallo"
C:\Users\Lukas>
My guess is that cmd.exe
stops using normal command line processing after it found a /C
within the command line and simply passes the remaining string to the command processor (as kinda stated in the documentation). If this is true there is no solution to your problem except of the (ugly) work-around to handle cmd.exe
differently within your wrapper.
As the other answers already point out, cmd
stops parsing the arguments when it encounters a /C
or /K
switch and passes the whole remaining string to the command processor.
Why? Because the string after /C
//K
is considered as another full command line, so the arguments do not belong to cmd
.
For instance, we have a command line like:
cmd /S /C del /Q "any file.txt"
So /S
and /C
are both arguments for cmd
, but del
, /Q
,... are not. Everything after /C
is kept together and treated as a separate command line:
del /Q "any file.txt"
The arguments /Q
and "any file.txt"
belong to the command del
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With