How do I deal with quote characters when using cmd.exe
Asked Answered
H

2

71

I'm trying to do this:

cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out"

However, I have problems which are down to the way cmd.exe works. If you read the help for it, it handles " characters in a special way. See the help at the end of question. So, this doesn't execute correctly... I'm guessing cmd.exe strips some quotes which makes the statement ill-formed.

I can do this successfully:

// quotes not required around folder with no spaces
cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > C:\temp\FolderWithNoSpaces\SomeProgram.out

But, I really need the first one to work. Is there away around the strange quote processing that cmd.exe uses? I want it to preserve all of the quotes, but there doesn't appear to be an option to make it do that.


Help taken from output of: 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
      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.
Hereditable answered 10/12, 2008 at 13:10 Comment(4)
When I try an equivalent example it works without /S. I copied c:\windows\system32\replace.exe to c:\ and renamed the copy "rep lace.exe" so it has a space in it. replace when executed with no parameters gives 2 lines one to stderr and one to stdout. so it's a neat example. C:\>cmd /c "rep lace" > "a a a" <--- that worked the same as without cmd /c. I'm interested in how cmd parses things, do you have any idea why the discrepancy between your example and my example? or a way I can recreate your example? taTesstessa
@Tesstessa - Please try harder yourself first. Why don't you try making your example look more similar to mine? Stick replace in a folder within program files, type ".exe" on the end of the name and output into a file that's in a folder containing spaces. See what happens then.Hereditable
here pastebin.com/raw.php?i=YtwQXTGN works fine for the case you gaveTesstessa
Hint: Use @ as a command start character to avoid quotes stripping: cmd.exe /c @"..." ...Firmin
H
94

Ah. doh. Think I've answered my own question.

If you use /S, and wrap the whole thing in quotes, it just removes those outer quotes.

cmd.exe /S /C " do what you like here, quotes within the outermost quotes will be preserved "
Hereditable answered 10/12, 2008 at 13:21 Comment(3)
So basically with the /S switch, you wrap all the file/executable paths in quotes, and then the entire command in another set of quotes. Worked for me, thanks!Radioactivate
Good lord. cmd /? help is worth less than nothing. It actually obfuscated this very simple answer.Fervidor
I'm curious to see an example of where quotes get eaten. If I do cmd /c echo "abc"msg"def", the quotes are preserved. And if I do it with /s. cmd /s /c "abc"msg"def" the quotes are still preserved.Tesstessa
T
13

I think you'll find that your example works absolutely fine as it is.

cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out"

I have reproduced your example here http://pastebin.com/raw.php?i=YtwQXTGN

C:\>cmd /c "c:\Program Files\my folder\my long program.exe" > "c:\temp\spaces are here\a.a"

C:\>type "c:\temp\spaces are here\a.a"
my long program.exe has run

C:\>

further example demonstrating it works with "my long program.exe", removing cmd /c, it operates fine too.

C:\>"c:\Program Files\my folder\my long program.exe" > "c:\temp\spaces are here\
a.a"

C:\>type "c:\temp\spaces are here\a.a"
my long program.exe has run

C:\>



Another example, but with replace.  replace with no parameters says "source path required"  "no files replaced"

C:\>replace > a.a
Source path required

C:\>type a.a
No files replaced

Exactly the same effect when they're in folders with spaces.

C:\>cmd /c "c:\Program Files\my folder\replace.exe" > "c:\temp\spaces are here\r.r"
Source path required

C:\>type "c:\temp\spaces are here\r.r"
No files replaced

C:\>

further demonstration with replace
without cmd /c works fine too.

C:\>"c:\Program Files\my folder\replace.exe" > "c:\temp\spaces are here\r.r"
Source path required

C:\>type "c:\temp\spaces are here\r.r"
No files replaced

C:\>

The reason why your example works fine

cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out"

and how/why it works the way it does, is because the > is interpreted as special by the host.exe So this part cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" - I think - is evaluated first. i.e. cmd /c does not see the > and after.

cmd /? shows 2 cases

Case 1 and Case 2. Your example fits Case 1

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.

You can test for sure that your example fits case 1, because if you add /s (without adding any more quotes or making any change at all to your example other than adding /s), then you get a different result, because it makes your example hit case 2. So that proves that your example is definitely a case 1. And it clearly meets all the criteria of case 1. If your example were a case 2, and you added /s, then it'd make no difference.

Your answer is interesting because it shows an alternative way of getting your result, but in case 2. By adding additional outter quotes and adding /s.

But actually, when you add those additional outter quotes, then you've just made it a case 2, and adding a /s on top of that won't make a difference.

C:\>cmd /c "c:\Program Files\my folder\replace.exe"
Source path required
No files replaced

C:\>cmd /s /c "c:\Program Files\my folder\replace.exe"
'c:\Program' is not recognized as an internal or external command,
operable program or batch file.

C:\>cmd /c ""c:\Program Files\my folder\replace.exe""
Source path required
No files replaced

C:\>cmd /s /c ""c:\Program Files\my folder\replace.exe""
Source path required
No files replaced

C:\>

The example in your question worked fine

cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out"

Your alternative (with the /S and outer quotes) you give as an answer to make the example work, works fine too

cmd.exe /S /C ""C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out""

Though your answer which is an alternative, can actually be simplified by removing the /S because it's already a case 2, so adding /s won't make any difference. So this would improve the solution given in your answer

cmd.exe /C ""C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out""

Your example which you described as a problem in your question, and your solution, produce the same good result. But one big difference I suppose, (and I am not sure how to test for it), but one difference in the way your example works, and the way the solution in your answer works, is I think in the case of your example, the hosting/invoking cmd.exe does the redirect to the file. Whereas in your solution's example, the invoked cmd.exe is passed the > by the host cmd.exe, and so the invoked cmd.exe does the redirect. Also of course, your example is a case 1, while your solution is an amendment you made (very well) to make it work in case 2.

I hope I haven't erred here, I may have. But your question and answer did help me wrap my head around how cmd and in particular cmd /c is working!

Perhaps your example was an oversimplification of your actual one, and your actual one did fail and needed your amendment. If your example case, had been a tiny bit more complex, by for example, having a parameter to the program that took quotes, then it'd fail Case 1, and you would indeed need outter quotes (/S would not change the result, so no /S would be necessary, as it'd already be a case 2 once you add those needed outer quotes). But the example you gave in your question actually seems to me to work fine.

Added - A related Q and A What is `cmd /s` for?

Tesstessa answered 18/12, 2012 at 18:36 Comment(10)
Very detailed investigation. You may be right, I did simplify the question some considerable time after posting it. I saw this question was being viewed a lot, so thought I'd try and remove irrelevant details... but perhaps they were relevant. Please look at the history of the question to see the original.Hereditable
@ScottLangham it looks like the original may also have had the oversimplification with "someprogram" and "folder containing spaces" and no parameter to "someprogram". Exactly 2 quotes before the redirect. I think that if your "some program" had a parameter with quotes, and then redirected, then it'd hit the Case 2 described in cmd /?, and would need outter quotes. (though still would not need /S). But your example, which seems to even be in the original, of cmd /c "some program" > ... That is still fitting Case 1, and works fine.Tesstessa
@ScottLangham If you'd have accidentally added a third quote before the redirect (which I guess could happen in a programming situation with escaped quotes as it can look complicated), then that'd also cause it to be a case 2(as there would be more than 2 quotes), and then you'd have needed outter quotes to get it to behave right. (though still no /s, as /s is just to make a case 1 go to case 2, and doesn't change the behavior of that which is already case 2).Tesstessa
@ScottLangham I may be making mistakes here too, as it is a bit tricky. And we can't directly test your examples. An ideal thing in that old question of yours would've been an example using a standard command like replace.exe where one can just copy/paste the example, and get the result you spoke of. And I can only guess as to why your actual example(pre any oversimplification so not "some folder" etc) would've hit a case 2. (I guess you had another 1+ quotes before the redirect. Hence >2 quotes before the redirect hence not fitting case 1 and going to case 2.Tesstessa
It's all too complicated for me. If in doubt, I say use the /S and put an extra pair of quotes around... at least you reliably know what you're getting.Hereditable
There's also another subtlety here: with the wrapping quotes, the redirection is performed by the subshell (the new instance of cmd.exe) and without the wrapping quotes the redirection is performed by the existing shell. Most of the time I don't think that would make any difference, but there may be edge cases.Vacate
@HarryJohnston yes I spotted that, the example in his question, prior to any extra wrapping quotes. I wrote "> is interpreted as special by the host (cmd) exe". His cmd /c only sees that which is before the >, just cmd /c "C:\Program Files\Somewhere\SomeProgram.exe" So given the circumstances, it is/was a simple cmd /c case and should've worked as is, or would've if that example embodied the nature of the actual cmd /c problem he was grappling with. I guess if doing start..run..cmd /c or /k ___, then there is no host cmd.Tesstessa
When calling cmd /c "c:\program files\xxx\some.exe" > "c:\path with spaces\output.out" from the win32 CreateProcess Api, the result is an error message (paraphrased as): "c:\program cannot be found". In this case, the /s and extra set of quotes was required to get the program to run. Equivalent error was seen whether the redirection ">" part was included or not. I suspect this would also be the case when invoked from a .bat file, but didn't try it.Upspring
@Upspring I don't know what you mean. You already have two pairs of quotes there in the example you give. Do you mean a third pair? or four pairs? Or are you just saying that when a path has spaces it need quotes, but then was anybody suggesting writing a path with spaces without quotes.Tesstessa
@Tesstessa - what I mean is cmd /c "c:\program files\xxx\some.exe" > "c:\path with spaces\output.out" failed , while cmd /s /c ""c:\program files\xxx\some.exe" > "c:\path with spaces\output.out"" succeeded. I suspect as you note that the /s is not strictly necessary, but it seems the "extra" quotes are necessary. I don't have an explanation for this, since there shouldn't be any extra quote stripping by an "outer" shell process here.Upspring

© 2022 - 2024 — McMap. All rights reserved.