What is `cmd /s` for?
Asked Answered
C

3

40

The Windows command prompt (cmd.exe) has an optional /s parameter, which modifies the behavior of /c (run a particular command and then exit) or /k (run a particular command and then show a shell prompt). This /s parameter evidently has something to do with some arcane quote handling.

The docs are confusing, but as far as I can tell, when you do cmd /csomething, and the something contains quotation marks, then by default cmd will sometimes strip off those quotes, and /s tells it to leave them alone.

What I don't understand is when the quote removal would break anything, because that's the only time /s ("suppress the default quote-removal behavior") would be necessary. It only removes quotes under a certain arcane set of conditions, and one of those conditions is that the first character after the /c must be a quotation mark. So it's not removing quotes around arguments; it's either removing quotes around the path to the EXE you're running, or around the entire command line (or possibly around the first half of the command line, which would be bizarre).

  • If the path to the EXE is quoted, e.g. cmd /c "c:\tools\foo.exe" arg1 arg2, then quotes are unnecessary, and if cmd wants to remove them, fine. (It won't remove them if the path has a space in the name -- that's another of the arcane rules.) I can't imagine any reason to suppress the quote removal, so /s seems unnecessary.
  • If the entire command line is quoted, e.g. cmd /c "foo.exe arg1 arg2", then it seems like quote removal would be a necessity, since there's no EXE named foo.exe arg1 arg2 on the system; so it seems like opting out of quote removal using /s would actually break things. (In actual fact, however, it does not break things: cmd /s /c "foo.exe arg1 arg2" works just fine.)

Is there some subtlety to /s that's eluding me? When would it ever be necessary? When would it even make any difference?

Chigger answered 26/3, 2012 at 5:20 Comment(7)
Here's someone who says /s helped him: #356488 I don't know exactly what what RunProgram() did/does, but trying to repro the problem on the command line got me nowhere. All I know is that cmd.exe's command parsing (especially with escaping characters) can be bizarre at times, so I have no doubt that /s is useful in at least one occasion.Nickolenicks
@MichaelBurr: I'm pretty sure he just needed the extra pair of quotes; the /s was redundant in this case, because the conditions under which /s makes a difference weren't met. But it doesn't do any harm, and it means the code would still work the same way no matter how the command being passed was changed.Hippodrome
I find it useful for the reasons given by @HarryJohnston. Also it is a programming question when programming using windows CMD shell scripts, so not sure why people are voting to close as "off topic".Retina
A related Q and A. #356488Erechtheus
According to ss64.com/nt/cmd.html , your description of how /s works is incorrect. However, none of the upvoted answers seem to correct you description, so I'm not sure what to think.Geralyngeraniaceous
In Windows 7 x64 it works like /s is always used.Tyishatyke
Hint: Use @ as a command start character to avoid quotes stripping: cmd.exe /c @"..." ...Tyishatyke
R
24

Cmd /S is very useful as it saves you having to worry about "quoting quotes". Recall that the /C argument means "execute this command as if I had typed it at the prompt, then quit".

So if you have a complicated command which you want to pass to CMD.exe you either have to remember CMD's argument quoting rules, and properly escape all of the quotes, or use /S, which triggers a special non-parsing rule of "Strip first and last " and treat all other characters as the command to execute unchanged".

With /S:

  • Always removes the first and last "
  • Never removes any other ", nor interprets escapes.

Without /S:

  • Complicated set of rules (for compatibility)
  • Sometimes removes quotes, sometimes not

You would use it where you want to take advantage of the capabilities of the CMD shell, rather than directly calling another program. For example environment variable expansion, output or input redirection, or using CMD.exe built-ins.

Example:

Use a shell built-in: This executes as-if you had typed DEL /Q/S "%TMP%\TestFile" at the prompt:

CMD.exe /S /C " DEL /Q/S "%TMP%\TestFile" "

This executes SomeCommand.exe redirecting standard output to a temp file and standard error to the same place:

CMD.exe /S /C " "%UserProfile%\SomeCommand.exe" > "%TMP%\TestOutput.txt" 2>&1 "

So what does /S give you extra? Mainly it saves you from having to worry about quoting the quotes. It also helps where you are unsure whether for example an environtment variable contains quote characters. Just say /S and put an extra quote at the beginning and end.

Vaguely Related: $* in Bourne Shell.

Some background

Recall that the list of arguments to main() is a C-ism and Unix-ism. The Unix/Linux shell (e.g. Bourne Shell etc) interprets the command line, un-quotes the arguments, expands wildcards like * to lists of files, and passes a list of arguments to the called program.

So if you say:

$ vi *.txt

The vi command sees for example these arguments:

vi
a.txt
b.txt
c.txt
d.txt

This is because unix/linux operates internally on the basis of "list of arguments".

Windows, which derives ultimately from CP/M and VAX, does not use this system internally. To the operating system, the command line is just a single string of characters. It is the responsibility of the called program to interpret the command line, expand file globs (* etc) and deal with unquoting quoted arguments.

So the arguments expected by C, have to be hacked up by the C runtime library. The operating system only supplies a single string with the arguments in, and if your language is not C (or even if it is) it may not be interpreted as space-separated arguments quoted according to shell rules, but as something completely different.

Retina answered 26/3, 2012 at 9:36 Comment(4)
Prior to the "some background", I don't see how what you say is correct. See here pastebin.com/raw.php?i=t60teCTk Try cmd /s /c ""c:\program files\replace.exe" > "c:\temp\my folder\a.a"" now remove the /s and it still works. So what's the point in /S?Erechtheus
@Erechtheus I agree; as I understand it, /S won't make a difference in these cases because there are more than two quotation marks. It matters if you follow /C with "executable file name that includes spaces" and then no other quotes. In that case, the quotes will be preserved around the file name unless you use /S. I added an answer expanding on this.Defraud
As a simple test, if you create a file called batch file.bat, then cmd /C "batch file" works (since it preserves the quotes), but cmd /S /C "batch file" fails because it strips the quotes and tries to execute a command called batch with argument file. But if you instead do cmd /C "batch file" "quoted_arg", then it fails since the first and last quote are stripped (with or without /S).Defraud
@TimGoodman well your timing is phenomenal.. i've just recently looked into /S and figured it out to a large extent!! dostips.com/forum/viewtopic.php?f=3&t=10516 though still leaves this question! dostips.com/forum/viewtopic.php?f=3&t=10518Erechtheus
H
17

Here's an example of how it can make a difference.

Suppose you have two executables: c:\Program.exe and c:\Program Files\foo.exe.

If you say

cmd /c "c:\Program Files\foo"

you'll run foo.exe (with no arguments) whereas if you say

cmd /s /c "c:\Program Files\foo"

you'll run Program.exe with Files\foo as the argument.

(Oddly enough, in the first example, if foo.exe didn't exist, Program.exe would run instead.)

Addendum: if you were to type

 c:\Program Files\foo

at the command prompt, you would run Program.exe (as happens with cmd /s /c) rather than foo.exe (as happens with just cmd /c). So one reason for using /s would be if you want to make sure a command is parsed in exactly the same way as if it were being typed at the command prompt. This is probably more likely to be desirable in the scenario in the question Michael Burr linked to, where cmd.exe is being launched by CreateProcess rather than from a batch file or the command line itself..

That is, if you say

CreateProcess("cmd.exe", "cmd /s /c \"" MY_COMMAND "\"", ...)

then the string MY_COMMAND will be parsed exactly as if it were typed at the command prompt. If you're taking command-line input from the user, or if you're a library processing a command line provided by an application, that's probably a good idea. For example, the C runtime library system() function might be implemented in this way.

Hippodrome answered 26/3, 2012 at 8:46 Comment(10)
You have to repeat the program name as part of createprocess' second argument. CreateProcess("cmd.exe", "CMD.exe /s /c \"" MY_COMMAND "\"", ...). The first says which executable to start, the second what the command line should be - but the executable is expected as the first part of the command line by convention, indeed the first argument can be omitted and will be deduced from the second argument (not recommended).Retina
@Ben: Yes, that's the recommended practice, but note that it isn't actually mandatory. I think my code fragment would work as written. (Anyone who is actually writing code, however, should follow Ben's advice.)Hippodrome
@Retina But as Harry points out, and I observed the same thing and agree, see my comments and answer to that question #356488 The /S in that question seems to be redundant. Can you show an example where /S is useful and performs a function? Indeed for cmd.exe /s /c "c:\program files\blah.exe.." it's useless and performs a function(when would one quote a path and not want to preserve spaces?).But where is it useful and functional? In cases of adding outter quotes,the /S does nothing.Erechtheus
@Retina here's an example of outter quotes, notice that /S does nothing in that situation Try it pastebin.com/raw.php?i=t60teCTk cmd /s /c ""c:\program files\replace.exe" > "c:\temp\my folder\a.a"" and still with outter quotes but removing the /S i.e. cmd /c ""c:\program files\replace.exe" > "c:\temp\my folder\a.a""Erechtheus
@barlop, the point of /S is if you don't know in advance whether the command has embedded quotes or not. If there are exactly two quotes on the command line it is treated differently by default if there are exactly two quote characters than if there are more or fewer. /S makes it be treated the same. It's documented: Just type "help cmd" on the command line.Retina
Harry Your comment is very good, i'll just quote it here under your answer, " it(adding /S to the outter quotes) doesn't do any harm, and it means the code would still work the same way no matter how the command (within the outter quotes) being passed was changed". Makes perfect sense. And so now I see Case 1 in cmd /? is for convenience.Erechtheus
@Retina yes I agree, I haven't tested for one quote though.Erechtheus
@Retina /S with outter quotes, will ensure it's always treated the same? or /S alone will ensure it's always treated the same? I think you mean the former, /S with outter quotes will. And if that is so, then remove the /S , now when will it not work the same?Erechtheus
@Retina I think you're right. And the answer to my question in the comment I just made, is, if the person does no quotes at all. To show your correctness, consider cmd /c commandlinestring with outter quotes and no /s, and where the command line string has a space in it and no quotes. Then adding outter quotes to that, would cause it to hit case 1 of cmd /?, and space would become literal instead of the special it would be. Whereas adding the /S with the outter quotes, would make it run the same as it would without /C. Making /S both useful and functional there. That may be the only case!Erechtheus
In order to run foo.exe with /s, you would have to use cmd /s /c ""C:\Program Files\foo"". The first and last double-quote are removed, and the rest is executed as-is.Geralyngeraniaceous
D
3

In all but one specific case, the /S won't actually make any difference.

The help for cmd.exe is accurate, if a bit complicated:

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.

I'd summarize as follows:

Normal behavior: If the rest of the command line after /K or /C starts with a quote, both that quote and the final quote are removed. (See exception below.) Other than that, no quotes are removed.

Exception: If the rest of the command line after /K or /C starts with a quote, followed by the name of an executable file, followed by another quote, AND if those are the only two quotes, AND if the file name contains spaces but contains no special characters, then the quotes are not removed (even though they normally would have been removed according to the rule above).

The only effect of /S is to override this one exception, so that the two quote characters are still removed in that case.


If you always use /S, you can forget about the exception and just remember the "normal" case. The downside is that cmd.exe /S /C "file name with spaces.exe" argument1 won't work without adding an extra set of quotes, whereas without /S it would have worked... until you decide to replace argument1 with "argument1".

Defraud answered 24/8, 2022 at 9:33 Comment(1)
wonderful, finally i could understand cmd /s thank youCommunicate

© 2022 - 2024 — McMap. All rights reserved.