Reputation: 34949
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 ||
?
Upvotes: 3
Views: 122
Reputation: 34949
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"
Upvotes: 4