3

I'm trying to do something similar to Get Visual Studio to run a T4 Template on every build using cmd's forfiles to transform each template in VS2008.

If I execute

forfiles /m "*.tt" /s /c "\"%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\1.2\TextTransform.exe\" @file"

then I get TextTransform.exe's error message (the screen of text explaining what to pass it as arguments).

If I instead execute

forfiles /m "*.tt" /s /c "cmd /c echo Transforming @path && \"%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\1.2\TextTransform.exe\" @file"

then it works perfectly.

In order to debug this, I created a simple command-line program called debugargs which simply prints the number of arguments it receives and their values. Then some experimentation shows that the first form of directly passing the command to forfiles causes the first argument to be swallowed. E.g.

forfiles /m "*.tt" /s /c "debugargs.exe 1 2 3"

gives output

2 arguments supplied
#1: 2
#2: 3

The documentation I've been able to find is quite sparse, and I don't see any mention of this as a possibility. Is it just an obscure bug, or am I missing something?

Community
  • 1
  • 1
Peter Taylor
  • 4,918
  • 1
  • 34
  • 59
  • Interesting. All the samples I see online use `/c "cmd /c ..."`; none of them call a separate exe directly. – Nate Hekman Apr 04 '13 at 17:35
  • 1
    Strange. I found that if EITHER of the first 2 parameters is an EXE then that EXE will run and receive the remaining arguments (ie param3+) If however the args are .BATs, then the SECOND will run, receiving params 3+ – Magoo Apr 04 '13 at 17:49
  • @PeterWright, interesting observation. It seems to be slightly more complicated. Looking only at cases where the second parameter is an `exe`: if the first parameter isn't a file, I get `ERROR: The system cannot find the file specified.`. If the first parameter is e.g. a text file I get `ERROR: "test.txt" is not a valid executable.` So the only dummy which really works there is an existing `.bat` or `.cmd` file. Bizarre. – Peter Taylor Apr 05 '13 at 10:12

3 Answers3

3

This appears to be a bug in the way forfiles invokes .exes. On a hunch I extended my debugargs program to print the full command line.

X:\MyProject>forfiles /m "*.tt" /s /c "debugargs.exe 1 2 @file"

2 arguments supplied
#1: 2
#2: Urls.tt
Full command line: 1 2 "Urls.tt"

So the most appropriate workaround would be to double the executable name:

forfiles /m "*.tt" /s /c "debugargs.exe debugargs.exe 1 2 @file"

The alternative workaround is to invoke with cmd /c. However, note here that if you need to quote the executable's path (e.g. because it contains a space), you'll need an extra workaround of prepending @:

forfiles /m "*.tt" /s /c "cmd /c @\"debugargs.exe\" 1 2 @file"
Peter Taylor
  • 4,918
  • 1
  • 34
  • 59
  • What is the purpose of the `@` character here? I wanted to use the `FORFILES` with an executable's path containing spaces and couldn't manage to make it work. It's like the `@` character enabled the use of the `\"` which I already tried but without the `@` and without success. Is there a place where I could have more information about this property? Thanks – simpLE MAn Sep 11 '17 at 19:17
  • Sorry, I can't remember. I don't use cmd at all often: for preference I use cygwin bash. – Peter Taylor Sep 11 '17 at 21:05
0

I reproduced the behavior in forfiles as well. You can work around by using cmd /c before the command, or you could transition to PowerShell, where the equivalent command would be something like this (not tested):

get-childitem . -filter "*.tt" -recurse | foreach-object {
  & "${ENV:CommonProgramFiles(x86)}\Microsoft Shared\TextTemplating\1.2\TextTransform.exe" "`"$($_.Name)`""
}

Bill

Bill_Stewart
  • 22,916
  • 4
  • 51
  • 62
  • I don't think PowerShell is an option (unless I invoke it from `cmd`), because VS2008 dumps the command I give it into a batch file to execute. – Peter Taylor Apr 04 '13 at 17:55
0

I've been struggling with this as well. The work-around I've found is to just add an extra space between the command and first argument! So where I was trying to do:

FORFILES /s /m *.dll /c "python \"c:\path\to\script.py\" -t arg1 etc"

python was trying to find file "arg1" to execute, but if I just change it to:

FORFILES /s /m *.dll /c "python  \"c:\path\to\script.py\" -t arg1 etc"

this actually works!

jamieg
  • 189
  • 6