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%%
)