有人可以向我解释为什么以下代码段打印0
:
@echo off
setlocal
for /f %%i in ('cmd /c echo blah') do (
echo %%i | findstr bin > NUL
echo %ERRORLEVEL%
)
在for-loop之外添加另一个等效语句时,会打印1 1
:
@echo off
setlocal
echo blah | findstr bin > NUL
echo %ERRORLEVEL%
for /f %%i in ('cmd /c echo blah') do (
echo %%i | findstr bin > NUL
echo %ERRORLEVEL%
)
我对Batch有点新手,所以这对我来说有点神秘,因为这两个陈述似乎是无关的。任何有关这方面的帮助将不胜感激,谢谢!
答案 0 :(得分:5)
问题是,在code block
(带括号的系列语句)中,任何%var%
都会被parse time
处变量的实际值所取代。
欣赏您的第一个示例,%errorlevel%
0
,并如此回应。在第二个示例中,遇到1
时为for
,因此它被1
替换。
如果要显示可在循环中更改的环境变量的值,则需要执行以下三项操作之一:
调用setlocal enabledelayedexpansion
并回显!var!
而不是%var%
- 注意您可以激活的嵌套setlocal
指令数量有限。
调用子程序
使用syntax-exploit。
关于delayedexpansion
的论文有很多很多文章。
粗略地说,您可以简单地使用(注意 - 案例在批处理中基本上无关紧要,除了循环控制变量的情况(在此实例中为metavariable
- %%i
)
@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!
)
另一种方法是动态调用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
)
这样做的缺点是endlocal
支持自上次setlocal
以来对环境所做的任何更改。另请注意,如果delayedexpansion
无效,则!
不再是特殊字符。
或者,您可以以传统方式使用errorlevel
:
@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
)
)
请注意,这会查看run-time
的{{1}}值,errorlevel
表示"如果errorlevel为n 或大于n &#34 ;
或 - 调用子程序:
if errorlevel n
请注意@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
(此处冒号很重要 - 这意味着"转到物理文件末尾"
或者,使用子程序的特殊版本 - 语法 - 利用
goto :eof
答案 1 :(得分:0)
更新:
如果您在for语句的结束echo %ERRORLEVEL%
之后放置)
语句,它将按预期工作。
for /f %%i in ('cmd /c echo blah') do (
echo %%i | findstr bin > NUL
)
echo %ERRORLEVEL%
我有点猜测,因为确切的'确定'原因,但setlocal
肯定会导致它。我假设for循环中的ERRORLEVEL值是cmd /c ...
语句的结果。这似乎是一种奇怪的行为,但我在批处理文件中看到的情况更糟。
如果您在开头删除了setlocal
,它就会像通常期望的那样工作..
ORIGINAL:
findstr
在运行时设置errorlevel
。因此,echo %errorlevel%
将始终输出0.
例如,以下findstr语句成功,因此它输出带匹配的行(传递给它的唯一行):
C:\>echo blahbin | findstr bin
blahbin
C:\>echo %errorlevel%
0 ; success / found it..
虽然此语句不成功(findstr不输出任何内容),并且errorlevel设置为1:
C:\>echo blah_in | findstr bin
C:\>echo %errorlevel%
1 ; failed
答案 2 :(得分:0)
@echo off
setlocal ENABLEDELAYEDEXPANSION
for /f %%i in ('cmd /c echo blah') do (
echo %%i | findstr bin > NUL
echo !ERRORLEVEL!
)
这应该有效。
cmd中的for循环在技术上是一行,因此变量只展开一次。使用感叹号而不是百分比和" ENABLEDELAYEDEXPANSION"应该解决它。
答案 3 :(得分:0)
@echo off
setlocal enabledelayedexpansion
:::This part is not exactly necessary but it creates the file dictionary.txt and the var - this
echo.word>dictionary.txt
set this=notaword
:::So instead of checking for errorlevel just check for a space in the output
:middle
for /f "tokens=*" %%a in ('findstr /n /b /c:%this% dictionary.txt') do (
set "output=%%a"
goto :yeah
)
:yeah
if exist %output% (""," "
goto :oops
) else (
goto :nice
)
:oops
echo.%this% is NOT a word
pause & set this=word & goto :middle
:nice
echo.%output%
pause