我正在编写一个批处理脚本,它将循环遍历文本文件的每一行,(每行包含一个文件名)检查文件是否存在,然后运行该文件并移动它。
这是我的批处理脚本:
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (./ready/input.txt) DO (
ECHO.
ECHO.
ECHO.
ECHO Check %%i exists, set error flag if it doesnt
if not exist .\ready\%%i set errorlevel=2
echo return code is %errorlevel%
ECHO Run %%i if it exists
if errorlevel 0 call .\ready\%%i
ECHO Move %%i to archive if no error occured
if errorlevel 0 copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i
ECHO Copy line of text to the new output.txt file if an error occured
if %errorlevel% NEQ 0 >>output.txt %%i, %%j, %%k
)
这是输出:
我不明白为什么“if errorlevel”没有按预期工作...如果文件不存在(如本示例中不存在),则不应尝试运行该文件,也不应复制文件,它应该回显 2 而不是 0
编辑1:我正在阅读另一篇关于“延迟环境变量扩展”的SO帖子,我不确定这个问题是否相关
ERRORLEVEL
和 %ERRORLEVEL%
是两个不同的变量。这意味着您使用 echo return code is %errorlevel%
和 if %errorlevel% NEQ 0 >>output.txt %%i, %%j, %%k
的代码可能是错误的。
ERRORLEVEL
是内置的,用于获取最后一个命令的结果。您可以像这样使用它:
IF ERRORLEVEL 1 ECHO error level is 1 or more
ERRORLEVEL
不能设置,就像bash不允许你set ?= ...
%ERRORLEVEL%
是一个环境变量。如果设置了 %ERRORLEVEL%
,那么当您使用 %ERRORLEVEL%
时,它会在脚本中使用。如果未设置 %ERRORLEVEL%
AND if 命令扩展已启用,则它回退到ERRORLEVEL
。
ERRORLEVEL
不更新%ERRORLEVEL%
。Raymond Chen 有一篇很好的博客文章:
ERRORLEVEL is not %ERRORLEVEL%
@ECHO OFF
SETLOCAL
DEL output.txt 2>nul
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (.\ready\input.txt) DO (
ECHO.
ECHO.
ECHO.
ECHO Check %%i exists, set error flag if it doesnt
if exist .\ready\%%i (set "errorflag=") ELSE (set errorflag=2)
CALL echo return code is %%errorflag%%
ECHO Run %%i if it exists
if NOT DEFINED errorflag (
call .\ready\%%i
ECHO Move %%i to archive if no error occured
if errorlevel 1 (SET errorflag=3) ELSE (ECHO copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i)
)
ECHO Copy line of text to the new output.txt file if an error occured
if DEFINED errorflag >>output.txt ECHO %%i, %%j, %%k
)
GOTO :EOF
这是一个重写的过程。
注意:
output.txt
在开始时被删除,否则
>>
将附加到任何现有文件中。
2>nul
如果删除失败(例如文件不存在),会抑制错误消息在块语句
(a parenthesised series of statements)
中,整个块被解析并执行THEN。块中的任何
%var%
都将被该变量的值替换在块被解析时 - 在执行块之前。 因此,
IF (something) else (somethingelse)
将使用遇到
%variables%
时的
IF
的值来执行。克服此问题的两种常见方法是 1) 使用
setlocal enabledelayedexpansion
并使用
!var!
代替
%var%
来访问
var
的更改值或 2) 调用子例程以使用更改后的值执行进一步处理.因此请注意使用
CALL ECHO %%var%%
,它显示
var
的更改值。显示
CALL ECHO %%errorlevel%%
,但遗憾的是随后重置错误级别。如果
IF DEFINED var
已定义为
CURRENTLY,则
var
为 true。
ERRORLEVEL
是一个特殊的变量名。它由系统设置,但如果由用户设置,则用户分配的值会覆盖系统值。如果
IF ERRORLEVEL n
是 n
或大于 n,则
errorlevel
为 TRUE。因此,IF ERRORLEVEL 0
始终为真。语法
SET "var=value"
(其中值可以为空)用于确保行末尾的任何杂散空格不包含在分配的值中。所需命令仅出于测试目的而进行
ECHO
编辑。验证命令正确后,将
ECHO COPY
更改为
COPY
以实际复制文件。我使用了以下
input.txt
:
seterr1.bat, J1, K1
seterr5.bat,J2,K2
seterr0.bat,J3 K3
seterr5.bat, J4, K4
notexist.bat, J5, K5
现有文件
seterr*.bat
其中包含
@ECHO OFF
EXIT /b 1
(最后一行中的
1
决定返回的
errorlevel
)并收到结果输出:
Check seterr1.bat exists, set error flag if it doesnt
return code is
Run seterr1.bat if it exists
Move seterr1.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured
Check seterr5.bat exists, set error flag if it doesnt
return code is
Run seterr5.bat if it exists
Move seterr5.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured
Check seterr0.bat exists, set error flag if it doesnt
return code is
Run seterr0.bat if it exists
Move seterr0.bat to archive if no error occured
copy .\ready\seterr0.bat .\archive\__J3_K3_seterr0.bat
Copy line of text to the new output.txt file if an error occured
Check seterr5.bat exists, set error flag if it doesnt
return code is
Run seterr5.bat if it exists
Move seterr5.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured
Check notexist.bat exists, set error flag if it doesnt
return code is 2
Run notexist.bat if it exists
Copy line of text to the new output.txt file if an error occured
请注意,正如我之前提到的,副本只是
ECHO
编辑的。和输出.txt
seterr1.bat, J1, K1
seterr5.bat, J2, K2
seterr5.bat, J4, K4
notexist.bat, J5, K5
:return
ECHO @exit /b %1 >ret.cmd
CALL ret.cmd
GOTO :eof
然后像这样使用它:
:Attempt
SETLOCAL
CALL somethingThatFails
SET retcode=!errorlevel!
CALL somethingThatPasses : don't care about the errorlevel here
CALL :return !retcode!
ENDLOCAL
CALL :eof
所以,整个事情看起来像这样:
测试.cmd...
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
CALL :Attempt
IF !errorlevel! NEQ 0 (ECHO Attempt Failed) ELSE (ECHO Attempt succeeded!)
GOTO :eof
:Attempt
SETLOCAL
CALL somethingThatFails
SET retcode=!errorlevel!
CALL somethingThatPasses : don't care about the errorlevel here
CALL :return %retcode%
ENDLOCAL
CALL :eof
:return
ECHO @exit /b %1 >return.cmd
CALL ret.bat
GOTO :eof
somethingthatfails.cmd...
DIR some command that fails >nul 2>&1
通过.cmd 的东西...
DIR >nul 2>&1
这样做的一个副作用是生成一个名为 ret.cmd 的文件。我通常使用 :end 子例程来进行清理并删除它。
%errorlevel%
,这是我几年前学到的一个技巧:
:: force errorlevel to 1
@(call)
echo %errorlevel%
:: force errorlevel to 0
@(call )
echo %errorlevel%
pause
call
后面需要空格才能将
%errorlevel%
设置为
0
。
更新:研究后,我找到了参考这里。
ver >nul
ver.exe 始终返回 0。
cmd /c exit 2
就可以设置
errorlevel
并在批处理文件中本地使用它,甚至在它结束后询问外面的
errorlevel
:
set errorlevel=2
:
cmd /c exit %errorlevel%
:
if errorlevel 3 echo 3
if errorlevel 2 echo 2
if errorlevel 1 echo 1
if errorlevel 1 echo 0
结果
>test.bat
2
1
0
>if errorlevel 2 echo 2
2
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (.\ready\input.txt) DO (
ECHO.
ECHO.
ECHO.
ECHO Check %%i exists, execute it if it does
if exist .\ready\%%i (
call .\ready\%%i
ECHO Move %%i to archive if no error occured
if not errorlevel 1 (
copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i
) else (
ECHO Copy line of text to the new output.txt file if an error occurred
>>output.txt %%i, %%j, %%k
)
)
)
此行会将 ERRORLEVEL 设置为 1,适用于 DOS 或更高版本:
<nul find ""
if errorlevel 1 echo ERRORLEVEL is 1 or more
此行会将 ERRORLEVEL 设置为 0,适用于 DOS 或更高版本:
echo a |find "a" >nul
if errorlevel==0 echo No Error