The issue is that within a code block
(parenthesised series of statements) any %var%
will be replaced by the actual value of the variable at parse time
.
Hin your first example, %errorlevel%
0
and echoed as such. In second example, it is 1
when the for
is encountered, hence it it replaced by 1
.
If you want to display a value of an environment variable that may be changed within a loop, then you need to do one of three things:
Invoke setlocal enabledelayedexpansion
and echo !var!
instead of %var%
- noting that the number of nested setlocal
instructions you can have active is limited.
Call a subroutine
Employ a syntax-exploit.
There are many, many articles about delayedexpansion
on SO.
Crudely, you could simply use (note - case is largely irrelevant in batch, except for the case of the loop-control variable (metavariable
- %%i
in this instance)
@echo off
setlocal ENABLEDELAYEDEXPANSION
echo blah | findstr bin > NUL
echo %ERRORLEVEL% or !errorlevel!
for /f %%i in ('cmd /c echo blah') do (
echo %%i | findstr bin > NUL
echo %ERRORLEVEL% or !errorlevel!
)
Another way is to dynamically invoke setlocal
@echo off
setlocal
echo blah | findstr bin > NUL
echo %ERRORLEVEL% or !errorlevel!
for /f %%i in ('cmd /c echo blah') do (
echo %%i | findstr bin > NUL
setlocal ENABLEDELAYEDEXPANSION
echo %ERRORLEVEL% or !errorlevel!
endlocal
)
The disadvantage of this is that the endlocal
backs out any changes made to the environment since that last setlocal
. Also note that if delayedexpansion
is not in effect, !
is no longer a special character.
Or, you can use errorlevel
in its traditional manner:
@echo off
setlocal
echo blah | findstr bin > NUL
echo %ERRORLEVEL% or !errorlevel!
for /f %%i in ('cmd /c echo blah') do (
echo %%i | findstr bin > NUL
if errorlevel 1 (echo errorlevel is 1 or greater
) else (echo errorlevel is 0
)
)
Note that this looks at the run-time
value of errorlevel
and if errorlevel n
means "if errorlevel is n or greater than n"
Or - call a subroutine:
@echo off
setlocal
echo blah | findstr bin > NUL
echo %ERRORLEVEL% or !errorlevel!
for /f %%i in ('cmd /c echo blah') do (
echo %%i | findstr bin > NUL
call :show
)
goto :eof
:show
echo %ERRORLEVEL%
goto :eof
Note here that goto :eof
(here the colon is important - this means "go to the physical end-of-file"
Or, a special version of using a subroutine - a syntax-exploit
@echo off
setlocal
echo blah | findstr bin > NUL
echo %ERRORLEVEL% or !errorlevel!
for /f %%i in ('cmd /c echo blah') do (
echo %%i | findstr bin > NUL
call echo %%errorlevel%%
)