2

I had a batch file, which (when simplified) looked like this:

@Echo Off
SetLocal EnableDelayedExpansion
    MD "MyProgram^!"
    MD "MyProgram version 2"
    MD "MyProgram (next version)"
    MD "MyProgram O&O"  

    Del Folders.txt
    Call :AddFoldersRecursive .
EndLocal
Goto :EOF

:AddFoldersRecursive FolderPath
    Echo %~1>>Folders.txt
    For /D %%f In ("%~1\*") Do  Call :AddFoldersRecursive "%%~f"
Goto :EOF

It brought up all kinds of errors when facing different kinds of file names in different situations:

  • MyProgram version 2 couldn't be echoed, because 2>> was interpreted incorrectly.
  • MyProgram (next version) couldn't be echoed, because parentheses had to be ^ escaped.
  • MyProgram O&O couldn't be echoed, because of the & symbol
  • MyProgram! couldn't be echoed, because of the ! symbol

Is there any solution which works for all such situations (including combinations and nesting, etc.)?

(ASCII is fine -- I don't need Unicode support right now.)

user541686
  • 205,094
  • 128
  • 528
  • 886
  • Just wanted to leave a comment since you had asked this question as a comment on my last answer - but I don't see any _simple_ way to do this without doing some manual escaping. – Dominic K Aug 29 '11 at 03:21
  • @DMan: Interesting, thanks for the comment! – user541686 Aug 29 '11 at 03:30
  • I'll be amazed if anyone can solve this puzzle - cmd.exe batch files have the worst string handling and the most irregular, half-assed quoting support (apparently cobbled together ad-hoc and by trial and error over the years of implementing cmd.exe). I'm already amazed at the things people successfully do with batch files on Windows when it's more than a simple list of programs to run. I have a hideous monster of a batch file that lets me easily compile simple C/C++ programs with various compilers for testing. It seems to break anytime I just load it in an editor. I hate having to update it. – Michael Burr Aug 29 '11 at 03:46
  • Oh yeah - and thanks to Microsoft's decision to standardize on the name "Program Files (x86)", the parenthesis problem is one that has hit me in that batch file every now and again (and not just for "echo"). – Michael Burr Aug 29 '11 at 03:51
  • @Michael: Funny, I also have a batch file to do the same thing, and it *also* seems to break every once in a while. I'm actually really tempted to try and make a UNIX-style shell script interpreter for Windows with *proper* nesting and quoting, but it's obviously a lot of work... (And yeah, Program Files is another one...) – user541686 Aug 29 '11 at 03:51
  • I take it your script is sufficiently complicated that `dir /b /ad-l-h > files.txt` is insufficient? – Gabe Aug 29 '11 at 04:02
  • @Gabe: Yes -- for one thing, I should actually have said `%%~dpnxf` instead of `%%f`, because I need the full paths. I also do some checks inside the `For` loop, which I can't do with a `Dir`. – user541686 Aug 29 '11 at 04:10
  • 1
    Like Gabe, I can't reproduce this issue, the command you've specified works fine for me. What version of Windows are you using? Have you actually tried the simplified command to see if it works for you? – Harry Johnston Aug 29 '11 at 04:52
  • @Harry: You're right, I guess my on-the-spot simplification actually didn't reproduce the issue (probably because I inlined the subroutine I had). I've put up a sample script which demonstrates the problem. – user541686 Aug 29 '11 at 05:34
  • You only get problems when you use percent expansion, %%-FOR-Loop expansion can only fail with exclamation marks, ! delayed expansion can handle all characters – jeb Aug 29 '11 at 06:33

2 Answers2

4

Edit: Take 2...

For /D %%f In (*) Do (
    Set "Text=%%~dpnxf"
    SetLocal EnableDelayedExpansion
    Echo.!Text!>>files.txt
    EndLocal
)
Hand-E-Food
  • 12,368
  • 8
  • 45
  • 80
  • Already tried: That doesn't work for file names with exclamations, e.g. `Yahoo! Messenger` (yes, I still have it :P) – user541686 Aug 29 '11 at 03:49
  • You know, that block of code could be replaced by `Dir /B >>files.txt` to achieve the desired result. But I will endevour to answer the original question! – Hand-E-Food Aug 29 '11 at 04:01
  • Right, but that's only because I was simplifying. :) I'm actually echoing full paths and doing some checks, etc... thanks for the suggestion anyway, though! – user541686 Aug 29 '11 at 04:12
  • Okay, here's my next attempt. It seems to cover all of the cases you've listed, plus % symbols. – Hand-E-Food Aug 29 '11 at 06:14
  • Yes, the delayed toggling can handle all special characters, only the `echo.` can fail if there exists a file named `echo` (without extension), better use `echo(` it's safe and it's faster – jeb Aug 29 '11 at 06:29
  • @jeb, Hand-E-Food: For some reason, this works in one of my scripts but not in another one. (The `!` sometimes gets stripped off.) Let me see what's wrong and I'll post something when I find out. – user541686 Aug 29 '11 at 06:46
  • @jeb, Hand-E-Food: AHHHHHHH I **also** need to *disable* delayed expansion in my **`For`** loop's `In` clause!!! Doing that seems to make everything work fine now!! Thanks so much!! I didn't even believe it was possible! :D – user541686 Aug 29 '11 at 06:55
  • Yes, disable delayed expansion is required, it's obvious :-) To understand why you could read [how the parser works](http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/4095133#4095133), I spend many hours to discover this – jeb Aug 29 '11 at 08:11
0

Enabling delayed expansion eats the exclamation marks, so I disabled it for the output stage. What I did here was copy it to another variable so I could do replacement, then replace the & with ^&. This probably doesn't work for all valid filenames, but does work for your examples:

:AddFoldersRecursive FolderPath
SetLocal DisableDelayedExpansion
    set "x=%~1"
    Set "x=%x:&=^&%"
    >>Folders.txt Echo %x%
    For /D %%f In ("%~1\*") Do  Call :AddFoldersRecursive "%%~f"
EndLocal
Goto :EOF
Gabe
  • 84,912
  • 12
  • 139
  • 238
  • Good point, I think the quote example was a result of me messing with my file system a while ago (!), I didn't notice that. But see my sample script, for example, for an example with `&`, which is completely normal. – user541686 Aug 29 '11 at 05:38
  • Does it matter if you get quotes in for output file? If so, `Echo "%~1">>Folders.txt` should work. – Gabe Aug 29 '11 at 05:44
  • It definitely *does* matter!!! But that's not even the only issue. I've updated my question (*again*), this time with an example that demonstrates the `MyProgram version 2` problem as well. – user541686 Aug 29 '11 at 05:49
  • Nice, I just got a script that shows 3/4 of the problems. :D I don't think I can get 4/4 in a single example, but hopefully this is enough. – user541686 Aug 29 '11 at 05:54
  • OK, how about `>>Folders.txt Echo %~1`? – Gabe Aug 29 '11 at 05:58
  • That only gets rid of the `2>>` problem. The `!` problem and the `&` problem are still there. – user541686 Aug 29 '11 at 06:05
  • Yeah, I see. Well, if you look at my edit you'll see that I was able to get your examples to work, but only with significant complications. – Gabe Aug 29 '11 at 06:30
  • It's easier to enable the delayed expansion AFTER `set "x=%~1"`, then you can use `>>Folders.txt echo !x!` without any problems and no replacements for the special characters. – jeb Aug 29 '11 at 10:29