3

The set command with a variable name (prefix) sets the exit code to a non-zero value in case there is no matching variable defined; the exit code can be queried by conditional execution operators:

set VAR && echo Yes. || echo No!!

The above would return Yes. if VAR (or VARiable, etc.) is defined, and No!! otherwise, together with the error message Environment variable VAR not defined from the set command.

However, when I try to suppress the error message, No!! is always echoed, independent on whether or not VAR is defined:

set VAR 2> nul && echo Yes. || echo No!!

The outcome is the same when I execute the command line in Command Prompt or in a batch file.

Why does insertion of the redirection part (2> nul) change the exit code received by the operators && and ||?

aschipfl
  • 28,946
  • 10
  • 45
  • 77
  • 2
    Using parens changes the behavior `(set VAR) 2>nul &&echo Yes.|| echo No!!` - or - `(set VAR 2>nul) &&echo Yes.|| echo No!!` also no space between `>` and `nul` => `set VAR 2>nul&&echo Yes.||echo No` –  Sep 03 '19 at 21:36
  • This is the general method of doing what you want: `If Defined VAR (Echo Yes) Else Echo No.` – Compo Sep 03 '19 at 21:37
  • 1
    @LotPings, thanks! it seems the space between `nul` and `&&` makes the difference. I just found out that `2> nul set VAR && ... || ...` also works as expected; seems there is a space treated as part of the variable name... – aschipfl Sep 03 '19 at 21:38

1 Answers1

4

Referring to the command line:

set VAR 2> nul && echo Yes. || echo No!!

It seems that the SPACE between nul and && becomes treated as part of the variable name, so set does not check variables whose names begin with VAR, but instead VAR + SPACE, which is apparently not defined.


I created a batch file with a lot of tests, incorporating the cases from the useful comment by user LotPings:

@echo on
@rem /* Execute test cases in a sub-routine twice,
@rem    once with variable `VAR` defined and once not: */
@for %%I in ("Value" "") do @(
    set "VAR=%%~I"
    echo/& < nul set /P ="VARIABLE: "
    if defined VAR (set VAR) else echo VAR=
    call :SUB
)
@goto :EOF

:SUB
@rem // This constitutes a list of test cases:
@echo/& echo UNQUOTED (VAR):
set VAR && echo Yes. || echo No!!
set VAR> nul && echo Yes. || echo No!!
set VAR > nul && echo Yes. || echo No!!
set VAR 2> nul && echo Yes. || echo No!!
(set VAR > nul) && echo Yes. || echo No!!
(set VAR 2> nul) && echo Yes. || echo No!!
set VAR > nul&& echo Yes. || echo No!!
set VAR 2> nul&& echo Yes. || echo No!!
> nul set VAR && echo Yes. || echo No!!
2> nul set VAR && echo Yes. || echo No!!
@echo/& echo QUOTED ("VAR"):
set "VAR" && echo Yes. || echo No!!
set "VAR"> nul && echo Yes. || echo No!!
set "VAR" > nul && echo Yes. || echo No!!
set "VAR" 2> nul && echo Yes. || echo No!!
(set "VAR" > nul) && echo Yes. || echo No!!
(set "VAR" 2> nul) && echo Yes. || echo No!!
set "VAR" > nul&& echo Yes. || echo No!!
set "VAR" 2> nul&& echo Yes. || echo No!!
> nul set "VAR" && echo Yes. || echo No!!
2> nul set "VAR" && echo Yes. || echo No!!
@goto :EOF

And this is the related console window output:

>>> test-set.bat

VARIABLE: VAR=Value

UNQUOTED (VAR):

>>> set VAR   && echo Yes.   || echo No!!
VAR=Value
Yes.

>>> set VAR  1>nul  && echo Yes.   || echo No!!
Yes.

>>> set VAR   1>nul  && echo Yes.   || echo No!!
Environment variable VAR   not defined
No!!

>>> set VAR   2>nul  && echo Yes.   || echo No!!
No!!

>>> (set VAR  1>nul )  && echo Yes.   || echo No!!
Yes.

>>> (set VAR  2>nul )  && echo Yes.   || echo No!!
VAR=Value
Yes.

>>> set VAR  1>nul  && echo Yes.   || echo No!!
Yes.

>>> set VAR  2>nul  && echo Yes.   || echo No!!
VAR=Value
Yes.

>>> set VAR  1>nul  && echo Yes.   || echo No!!
Yes.

>>> set VAR  2>nul  && echo Yes.   || echo No!!
VAR=Value
Yes.

QUOTED ("VAR"):

>>> set "VAR"   && echo Yes.   || echo No!!
VAR=Value
Yes.

>>> set "VAR"  1>nul  && echo Yes.   || echo No!!
Yes.

>>> set "VAR"   1>nul  && echo Yes.   || echo No!!
Yes.

>>> set "VAR"   2>nul  && echo Yes.   || echo No!!
VAR=Value
Yes.

>>> (set "VAR"  1>nul )  && echo Yes.   || echo No!!
Yes.

>>> (set "VAR"  2>nul )  && echo Yes.   || echo No!!
VAR=Value
Yes.

>>> set "VAR"  1>nul  && echo Yes.   || echo No!!
Yes.

>>> set "VAR"  2>nul  && echo Yes.   || echo No!!
VAR=Value
Yes.

>>> set "VAR"  1>nul  && echo Yes.   || echo No!!
Yes.

>>> set "VAR"  2>nul  && echo Yes.   || echo No!!
VAR=Value
Yes.

VARIABLE: VAR=

UNQUOTED (VAR):

>>> set VAR   && echo Yes.   || echo No!!
Environment variable VAR  not defined
No!!

>>> set VAR  1>nul  && echo Yes.   || echo No!!
Environment variable VAR  not defined
No!!

>>> set VAR   1>nul  && echo Yes.   || echo No!!
Environment variable VAR   not defined
No!!

>>> set VAR   2>nul  && echo Yes.   || echo No!!
No!!

>>> (set VAR  1>nul )  && echo Yes.   || echo No!!
Environment variable VAR  not defined
No!!

>>> (set VAR  2>nul )  && echo Yes.   || echo No!!
No!!

>>> set VAR  1>nul  && echo Yes.   || echo No!!
Environment variable VAR  not defined
No!!

>>> set VAR  2>nul  && echo Yes.   || echo No!!
No!!

>>> set VAR  1>nul  && echo Yes.   || echo No!!
Environment variable VAR  not defined
No!!

>>> set VAR  2>nul  && echo Yes.   || echo No!!
No!!

QUOTED ("VAR"):

>>> set "VAR"   && echo Yes.   || echo No!!
Environment variable VAR not defined
No!!

>>> set "VAR"  1>nul  && echo Yes.   || echo No!!
Environment variable VAR not defined
No!!

>>> set "VAR"   1>nul  && echo Yes.   || echo No!!
Environment variable VAR not defined
No!!

>>> set "VAR"   2>nul  && echo Yes.   || echo No!!
No!!

>>> (set "VAR"  1>nul )  && echo Yes.   || echo No!!
Environment variable VAR not defined
No!!

>>> (set "VAR"  2>nul )  && echo Yes.   || echo No!!
No!!

>>> set "VAR"  1>nul  && echo Yes.   || echo No!!
Environment variable VAR not defined
No!!

>>> set "VAR"  2>nul  && echo Yes.   || echo No!!
No!!

>>> set "VAR"  1>nul  && echo Yes.   || echo No!!
Environment variable VAR not defined
No!!

>>> set "VAR"  2>nul  && echo Yes.   || echo No!!
No!!

The only cases that fail are those:

set VAR > nul && echo Yes. || echo No!!
set VAR 2> nul && echo Yes. || echo No!!

When the command parser recognises and temporarily removes the redirection part (see description of phase 2 in How does the Windows Command Interpreter (CMD.EXE) parse scripts?), potential SPACEs before > and/or after nul are left behind, which seem to be treated as part of the variable name by set, unless the given variable name is quoted; just one SPACE in total seems to be tolerated though.

Take also a look at the error messages Environment variable VAR not defined with different numbers of SPACEs behind the variable name.


The set command seems to handle its arguments in a particular way:

@set "VAR=Value"
rem // No trailing spaces -- returns `VAR=Value`:
set VAR
rem // One trailing space -- returns `VAR=Value`:
set VAR 
rem // Two or more trailing spaces -- returns an ERROR!
set VAR  
rem // No trailing spaces -- returns `VAR=Value`:
set "VAR"
rem // One trailing space -- returns `VAR=Value`!?
set "VAR "
rem // Two or more trailing spaces -- returns an ERROR!
set "VAR  "

The unquoted syntax seems not to follow the standard rules for tokenisation where two or more consecutive token separators like the SPACE become combined into a single one.

The big surprise to me is that even the quoted syntax tolerances one SPACE, though it ignores all SPACEs following the closing " at least.

Even more surprising is the fact that some other text (like X) behind a SPACE still returns no error:

@set "VAR=Value"
rem // No spaces behind `X`, one in front -- returns `VAR=Value`!?
set VAR X
rem // No spaces behind `X`, two in front -- returns an ERROR!
set VAR  X
rem // No spaces behind `X`, one in front -- returns `VAR=Value`!?
set "VAR X"
rem // No spaces behind `X`, two in front -- returns an ERROR!
set "VAR  X"
aschipfl
  • 28,946
  • 10
  • 45
  • 77
  • 1
    You could also use `set "var=test"`, then `set VAR 2> nul && echo Yes. || echo No!!` results to YES – jeb Sep 03 '19 at 22:25
  • 1
    Good idea, @jeb! Seems that `set` handles its arguments particularly, I just tried `set VAR`, followed by one space, which passes (meaning `VAR=Value` is returned), but with more than one, it fails... – aschipfl Sep 03 '19 at 22:31
  • By the way, @jeb, when `VAR` is defined, `set VAR X` (only one space before `X` and none after!) returns `VAR=Value`, most surprisingly... – aschipfl Sep 03 '19 at 22:54
  • 1
    You should be aware that `set VAR` command does _NOT_ reset the errorlevel to 0 when `VAR` variable does exists; it just set the errorlevel to 1 when the variable does not exists, unless the batch file have `.cmd` extension as described in **Table 3** of [this answer](https://stackoverflow.com/a/34987886/778560). It seems that `set VAR` command also manage the "Exit Code" described in the same answer because `set VAR && echo Yes || echo No` construct does _NOT_ change an errorlevel value previously set to 1 or any value even if the construct prints "Yes". @jeb – Aacini Sep 04 '19 at 03:51