Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the Windows "cmd.exe" parse arguments differently?

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?

like image 617
Andy Li Avatar asked Dec 24 '15 06:12

Andy Li


People also ask

How does cmd.exe work?

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.

How do I pass a command line argument to exe?

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.

What is argument in cmd?

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.

How do I run an EXE from command line arguments in Windows?

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.


3 Answers

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:

  1. 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.
  2. 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]"
like image 144
Harry Johnston Avatar answered Nov 15 '22 04:11

Harry Johnston


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.

like image 33
Lukas Thomsen Avatar answered Nov 15 '22 03:11

Lukas Thomsen


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.

like image 38
aschipfl Avatar answered Nov 15 '22 03:11

aschipfl