Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Odd behaviour from FOR command with seemingly harmless space in it. Explanation?

I ran into an "odd" (to me) problem while writing a batch file, and hope someone can explain why it does what it does... I had a batch file that built-up a moderately complex command-line, and I wanted it to echo what it had built to the screen before executing so that I could check that it had been built correctly. There are other ways I could have done this1, but for various reasons I tried something of the form:

for %%a in (echo "") do %%~a complicated-command-line-with-parameters

The thought being that it would run the do part twice: once with %%a set to echo (displaying the command to the console) and once with it set to "": the expectation was that it would actually execute the command (using %%~a would strip the double-quotes and all that would be left is the command itself). However, while the echo worked, the command itself was not executed.

For a minimal replication, run the following from a command-prompt:

C:\>for %a in (echo "") do %~a dir

C:\>echo dir
dir

C:\> dir

As can be seen, it runs the echo command fine, but it seems that the space in front of dir prevents it from being executed.

Note, though, that manually running a command with a leading space is fine:

C:\> dir
 Volume in drive C is OS
 Volume Serial Number is A8BD-F861
...

and, were it somehow trying to run the command <space>dir, then it (probably) would have complained of a missing command:

C:\>" dir"
'" dir"' is not recognized as an internal or external command,
operable program or batch file.

Question: The space between %~a (which is an empty string) and the command to be run seems to be causing the whole line to be ignored... does anyone know why?

Note: It make no difference whether it is run from the command-prompt (as above) or from within a .BAT file (after changing %a to %%a etc.). Nor does it make any difference whether the command-to-be-run is a built-in command (e.g. dir as above) or a standalone program.

Further evidence that it is the space between %~a and the command that is causing the problem comes from the "fix" that I found:

C:\>for %a in ("echo " "") do %~adir

C:\>echo dir
dir

C:\>dir
 Volume in drive C is OS
 Volume Serial Number is A8BD-F861
...

By adding the "separating space" to the "echo " string inside the for command, and removing it from the do clause (...do %~adir), the command works as I originally expected (although I dislike not having a space in front of the command).

After skimming the (somewhat daunting) top answer to the question How does the Windows Command Interpreter (CMD.EXE) parse scripts? that SomethingDark helpfully linked to, a cleaner alternative that seems to work is:

C:\>for %a in (echo call) do %~a dir

C:\>echo dir
dir

C:\>call dir
 Volume in drive C is OS
 Volume Serial Number is A8BD-F861
...

There are probably idiomatic situations where having the extra call might affect something, but for the moment, it seems to work as desired.


1 I could have put @echo on just before the line inside the batch file, but that also causes the prompt (e.g. C:\MyDirectory\SubDir>) to be shown, which I didn't want. The other "standard fallback" is to just duplicate the line and stick echo in front of the first copy, but then it's too easy for them to get out-of-sync!

like image 267
TripeHound Avatar asked Aug 23 '19 11:08

TripeHound


Video Answer


1 Answers

SomethingDark already pointed to the explanation.

It's the command vs argument token splitting of a line.
In your case the command token is always %~a this will be replaced later, but even when its empty, the parser will not reevaluate the command token.

With your fix, the dir command is always part of the command token.
But when echo is prefixed it can still echo the remaining part.

like image 179
jeb Avatar answered Oct 25 '22 07:10

jeb