7

I have the following code snippet:

if "%ARGV:~,1%"==":" echo %ARGV% begins with a colon.

As long as variable ARGV contains a non-empty value, or correctly said, it is defined, everything works as expected, hence if the string in ARGV begins with a colon, the echo command is executed.

However, as soon as I clear variable ARGV, a syntax error arises:

echo was unexpected at this time.

What is going on here? The syntax is perfectly fine, but why does that command line fail?

Even one of the most helpful threads here, How does the Windows Command Interpreter (CMD.EXE) parse scripts?, for such things does not deliver an explanation for this behaviour.

When I do the same directly in command prompt, everything is in order. Moreover, when I try it using delayed expansion no error occurs either.

Community
  • 1
  • 1
aschipfl
  • 28,946
  • 10
  • 45
  • 77
  • 1
    I am sure Jeb or dbenham could explain it. If I assign the first character of ARGV to ARGV1 and then use ARGV1 in the IF command I get no error. I am sure it has something to do with expansion and the colon because the colon is used for lables. – Squashman Sep 07 '16 at 02:13
  • 3
    @Squashman - Indeed I can explain the result :-) But it has nothing to do with labels. See [my answer](http://stackoverflow.com/a/39360592/1012053) – dbenham Sep 07 '16 at 03:12
  • 1
    Really nice question, but dbenham was to fast with his answer for me – jeb Sep 07 '16 at 05:24
  • 2
    @Squashman If you assign the first character of an **empty** ARGV by using `set ARGV1=%ARGV:~,1%` then ARGV1 contains `~,1`, therefore ARGV1 will never be empty – jeb Sep 07 '16 at 08:05
  • Just out of curiosity, what output were you expecting when you tried to get the first character of an empty string? There are no characters to return. – SomethingDark Sep 07 '16 at 12:27
  • @SomethingDark: As the variable name might indicate, `ARGV` comes from command line argument in my full script (`set "ARGV=%~1"`), which may be empty, of course. (All this comes from a small routine that handles command line arguments. I solved the issue in my script by preceding `if not defined ARGV goto :NEXT`.) Logically I would expect nothing to be returned when `ARGV` is empty, but I (thought I) know `%ARGV:~,1%` expands to `~,1` in case of empty input, and that is it; but as I learned now it is not that easy... – aschipfl Sep 07 '16 at 13:05

1 Answers1

5

My companion answer to jeb's answer to "How does the Windows Command Interpreter (CMD.EXE) parse scripts?" does explain the behavior.

My companion answer gives the necessary details on how % expansion works to fully predict the behavior.

If you keep ECHO ON, then you can see the result of the expansion, and the error message makes sense:

test.bat

@echo on
@set "ARGV="
if "%ARGV:~,1%"==":" echo %ARGV% begins with a colon.

-- output --

C:\test>test
echo was unexpected at this time.

C:\test>if "~,1" echo  begins with a colon.


The important rules from my answer that explain the expansion result are:

1)(Percent) Starting from left, scan each character for %. If found then

  • 1.1 (escape %) ... not relevant
  • 1.2 (expand argument) ... not relevant
  • 1.3 (expand variable)
    • Else if command extensions are disabled then ... not relevant
    • Else if command extensions are enabled then Look at next string of characters, breaking before % : or <LF>, and call them VAR (may be an empty list). If VAR breaks before : and the subsequent character is % then include : as the last character in VAR and break before %.
      • If next character is % then Replace %VAR% with value of VAR (replace with nothing if VAR not defined) and continue scan
      • Else if next character is : then
        • If VAR is undefined then Remove %VAR: and continue scan.
        • ... Remainder is not relevant

Starting with

if "%ARGV:~,1%"==":" echo %ARGV% begins with a colon.

The variable expansion expands all of the following strings to nothing because the variable is undefined:

%ARGV:
%"==":
%ARGV%

And you are left with:

if "~,1" echo  begins with a colon.

It works with delayed expansion because the IF statement is parsed before delayed expansion (explained in jeb's answer within phase 2)

Everything works from the command line because the command line variable expansion does not remove the string when the variable is not defined. (loosely explained in jeb's answer near the bottom within CmdLineParser:, Phase1(Percent))

Community
  • 1
  • 1
dbenham
  • 119,153
  • 25
  • 226
  • 353
  • 1
    It's always amazing that this is so non intuitive and shows the really bad design of cmd.exe – jeb Sep 07 '16 at 08:01
  • So an empty string causes the closing `%` of `%ARGV:~,1%` not to be recognised? that is what I call a terrible design flaw! Thank you for the insight! – aschipfl Sep 07 '16 at 08:16
  • 1
    @aschipfl The closing `%` will be treated in this case again as an opening `%` – jeb Sep 08 '16 at 09:18