Sam Rueby
Sam Rueby

Reputation: 6127

How do you run a command with a full path from the Windows forfiles command?

I am trying to run the following command:

forfiles /p ..\Schemas /m *.xsd /c "cmd /c ""C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe @path /classes"""

However, it fails with:

ERROR: Invalid argument/option - 'Files'.
Type "FORFILES /?" for usage.

These also don't work:

forfiles /p ..\Schemas /m *.xsd /c "cmd /c \"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe\" @path /classes"

forfiles /p ..\Schemas /m *.xsd /c "cmd /c ^"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe^" @path /classes"

It's definitely failing because the path being passed to cmd contains spaces. You normally solve this by quoting the whole argument. However, there doesn't appear to be a way to pass the double quote.

So, how do you run a command with a full path from the Windows forfiles command?

Upvotes: 3

Views: 18904

Answers (2)

aschipfl
aschipfl

Reputation: 34919

In your first attempt, you tried to put (additional) quotation marks around the entire command line after cmd /c rather than the path to the executable only, which will fail as cmd tries to find and execute the whole line as a command. Hence you needed to move the closing quotes immediately after xsd.exe instead of at the end of the command line.

Nevertheless, in general, the main problem here is that cmd and forfiles handle quotation marks differently. forfiles uses \" to escape quoting, but cmd does not care about the backslash. In addition, in case your code appears within a parenthesised block of code, the literal parentheses in your path may cause trouble additionally if they do not appear quoted to cmd. Finally, cmd may attempt to strip off quotation marks (unexpectedly), which may cause even more problems.

To solve all this, use another method of providing literal quotation marks for the command line after /C: forfiles supports specifying characters by their hexadecimal code in 0xHH notation; so stating 0x22 hides the quotation marks from the parent cmd instance until the command line is actually executed:

forfiles /p ..\Schemas /m *.xsd /c "cmd /c 0x220x22C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe0x22 @path /classes0x22"

This results in the following command line to be executed (using "D:\Data\Schemas\sample.xsd" as an example value for @path):

cmd /c ""C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe" "D:\Data\Schemas\sample.xsd" /classes"

Finally, after stripping off the outer-most quotes by cmd, the following command line is executed:

"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe" "D:\Data\Schemas\sample.xsd" /classes

Note that @path, as well as all other file-name- and path-related @-style variables of forfiles expand to already quoted values.


Initially, I completely forgot about the fact that cmd tries to strip off quotes. Klitos Kyriacou's answer reminded me of that; so if you like my answer, please do not forget to give credits to them as well!

Upvotes: 4

Klitos Kyriacou
Klitos Kyriacou

Reputation: 11631

It's not forfiles that's at fault here; it's the way cmd.exe treats quotation marks. It's essentially trying to be too clever for its own good. The CMD /C command can take any arbitrary line as a command, including one that contains spaces; however, if the command line given after CMD /C starts with a quotation mark, it removes the first and last quotation marks from the line before it tries to execute it. Thus, these two lines are exactly equivalent:

cmd /c echo hello
cmd /c "echo hello"

Now the problem is that the quotation mark gets stripped off in the following command:

cmd /c "C:\Program Files\xyz\abc.exe" one two

To make it do what you want, you need to enclose the whole thing in quotes (the inner quotes don't need to be escaped):

cmd /c ""C:\Program Files\xyz\abc.exe" one two"

Now, you can put this into a FORFILES command. FORFILES requires quotation marks to be escaped with a \, so we need to write:

forfiles /c "cmd /c \"\"C:\Program Files\xyz\abc.exe\" one two\""

Or in your specific case:

forfiles /p ..\Schemas /m *.xsd /c "cmd /c \"\"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe\" @path /classes\""

(Note that all quotation marks for CMD are escaped, but the outer quotation marks are for FORFILES so are not escaped.)

Upvotes: 3

Related Questions