0

I am trying to write a batch file that takes a couple of arguments, followed by one or more optional filenames (which may include wildcards) and at some point processes each one of the optional filenames, but the for command keeps trying to expand them, so even so much as just printing them won’t work.

I checked the help (for /?), but there does not seem to be any switches to prevent this. I tried using single, double, and back-quotes as well as /f, but nothing seems to work.

The following command works as one would expect:

> for %i in (foo bar baz) do @echo %i
foo
bar
baz

The following command does not:

> for %i in (foo bar baz really?) do @echo %i
foo
bar
baz

The following, even less so:

> ren > reallyz
The syntax of the command is incorrect.

> dir /b really*
reallyz

> for %i in (foo bar baz really?) do @echo %i
foo
bar
baz
reallyz

Is there a way to get the Windows command-interpreter to treat the passed list as strings and not try to interpret it as filename wildcards?

Synetech
  • 9,643
  • 9
  • 64
  • 96
  • Did you try escaping the question mark with a caret "^" as in (foo bar baz really^?) You may have to fiddle with it as Windows idea of an escape character is convoluted and seems to change when run on the command line vs run from a batch file. – JimR Feb 04 '13 at 18:41
  • Yes, escaping it does not work (besides, even if it did, then the user would have to remember to escape them when calling the batch-file which of course is a non-ideal interface since it is inconsistent with other uses of passing filenames). – Synetech Feb 04 '13 at 18:46

1 Answers1

3

No - the simple FOR command will always expand the * and ? wild card characters.

You need to use a GOTO loop to do what you want. The SHIFT command enables access to all parameters.

@echo off
:argLoop
if "%~1" neq "" (
  echo %1
  shift /1
  goto :argLoop
)

Most likely you want to do more with the arguments then just print them. Typically you would store them in an "array" of variables for later use.

@echo off
setlocal

set argCnt=1
:argLoop
if "%~1" neq "" (
  set "arg.%argCnt%=%~1"
  set /a argCnt+=1
  shift /1
  goto :argLoop
)
set /a argCnt-=1

:: Show Args
setlocal enableDelayedExpansion
for /l %%N in (1 1 %argCnt%) do echo arg.%%N = !arg.%%N!
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • That’s good, and I had already considered it, but using `shift` is irrevocable (it permanently shifts the arguments and does not have a `/reset` switch or take negative arguments), therefore I can no longer access them (i.e., the first two mandatory arguments) later in the batch-file. I guess I could push the first two to env-vars as well, but because I need to use the path-string options (`%~dp1`, `%~n2`…), putting them in env-vars is unpleasant (you cannot do it with vars, i.e., `%~nfoobar%` won’t work for obvious reasons). I’d have to run it through yet another `for`, but it’s worth a shot… – Synetech Feb 04 '13 at 22:16
  • @Synetech - yep, that is why I suggested storing the values in variables. Additional FOR loops are wordy, but they are fast. If you know ahead of time what variants you need, you could save multiple copies of the values, each with different expansion modifiers. – dbenham Feb 04 '13 at 22:30
  • Okay, it looks like it works. I changed the start index for the optional-arg loop. I also had to use a *double*-for—loop to access each of the first two arguments (e.g., `for /l %%i in (2,1,2) do for %%j in (!arg.%%i!) do echo [%%~pnj]` for the second one) but at least it works.     (This has seriously complicated what was supposed to be a relatively simple batch-file, but it is still simpler than if I were to write a full, compiled C++ program—I would have to research integrating zip, 7zip, etc. functions at a minimum.)     Thanks. – Synetech Feb 04 '13 at 22:54