Search code examples
windowsbatch-filecmdforfiles

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?


Solution

  • 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!