最佳做法-测试批处理命令的ErrorLevel

时间:2019-01-10 17:26:28

标签: batch-file cmd

我的批处理文件运行几个注册表编辑命令,并且如果所述注册表编辑命令返回错误级别,我想实现一个条件。我的问题是:我应该将这些命令包装在函数中,还是在每个注册表命令之后有条件地创建错误级别?我对将这些命令包装到函数中的担心是,并非每个命令都会被检查。我想确保每个命令都得到检查,并返回错误级别。

用于将命令包装在函数中的代码块可能不正确或丢失。不确定在每个函数的结尾是否需要包含EXIT?这是一个多步骤批处理脚本的一部分。

TITLE Disable UAC
ECHO STEP 1: Disable User Account Control
reg add HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f
reg add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f

IF %ERRORLEVEL% NEQ 0 ECHO An error was found w/ error code of: %ERRORLEVEL%

:disable_uac
TITLE Disable UAC
ECHO STEP 1: Disable User Account Control
reg add HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f
reg add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f

call :disable_uac
IF %ERRORLEVEL% NEQ 0 ECHO An error was found w/ error code of: %ERRORLEVEL$

关于第一个代码块,我假设ERRORLEVEL条件只返回LAST命令的错误代码,而不是两者都返回?

更新代码:

@echo off
goto ErrorCheck

SetLocal EnableDelayedExpansion

ECHO STEP 1: Copy Folder
robocopy "C:\Users\!username!\Documents\WindowsSetup\Training_Videos" "C:\Users\!username!\Desktop\Training_Videos" /e /copyall
CMD /K

:ErrorCheck
title Disable UAC
for /F "useback skip=2 delims= eol=" %%I in ("%~f0") do (
    IF "%%~I" == ":ErrorCheck" exit /B
    ECHO %%I
    %%I >>"C:\errorlog.log"
    if errorlevel 1 exit /B & ECHO You have encountered an issue. Look at Error Log for more information
)

2 个答案:

答案 0 :(得分:1)

一个非常简单的解决方案,可以运行批处理文件中编写的多个命令,同时取消为处理 STDOUT 而编写的标准输出,并在退出批处理文件执行时取消为处理 STDERR 而编写的错误消息在退出代码为更大或等于的第一个命令或可执行文件上退出时,1为:

@echo off
goto MainCode

reg.exe add HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f
reg.exe add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f

:MainCode
title Disable UAC
for /F "useback skip=2 delims= eol=" %%I in ("%~f0") do (
    if "%%~I" == ":MainCode" exit /B
    echo %%I
    %%I >nul 2>nul
    if errorlevel 1 exit /B
)

由于 FOR 循环命令行上的skip=2和标签行:MainCode,此批处理文件执行批处理文件中第二行下方定义的所有非空行。 / p>

这些命令行不能包含具有所提供代码的环境变量引用,因为环境命令引用在执行之前不会被Windows命令处理器扩展。当然,在 FOR 循环执行期间,可以使用命令call强制对命令行进行额外的解析,以扩展环境变量引用。

goto MainCode:MainCode之间的命令行也不能包含重定向操作符,例如<>>>|。重定向是通过cmd.exe执行批处理文件来完成的。 Windows命令处理器必须在解析命令行之前已经识别出它们,然后才能执行。因此,当cmd.exe在执行前解析命令行%%I >nul 2>nul时,它将在执行当前分配给循环变量>nul的任何内容之前识别重定向2>nulI,但是cmd.exe将永远无法识别在分配给循环变量I的命令行上指定的重定向。

另请参阅How does the Windows Command Interpreter (CMD.EXE) parse scripts?

该示范批处理文件的执行在第一个命令/可执行程序退出时立即退出,其值更大或等于 1。命令 IF EXIT 不会修改变量errorlevel,因此调用过程将获取失败的命令/可执行文件的退出代码。

另请参阅:

批处理文件的退出代码为0,如果没有命令退出,其值更大或等于 1

FOR 在此处打开由cmd.exe指定的%~f0已经执行的批处理文件,由于选项{而扩展为驱动器+路径+名称+批处理文件本身的扩展名。 {1}}并逐行处理它。

必须使用选项/F才能将usebackq中包含的完整批处理文件名解释为应处理的行的文件名,而不是将完整的合格批处理文件名本身解释为要处理的字符串通过 FOR

选项"告诉 FOR 跳过打开文件的前两行,并开始处理第三行。

具有选项skip=2

FOR 总是忽略空行。在使用选项/F时, FOR 也会忽略以分号开头的行,因为/F是行定义的默认结尾。因此,eol=;用于定义行尾字符,从而不会忽略以分号开头的行。

FOR 默认情况下,使用常规空格和水平制表符作为字符串定界符,将从文件读取的每个非空行拆分为子字符串,并且仅将第一个空格/制表符分隔的字符串分配给指定的循环变量{{ 1}}。这里不需要这种行拆分行为。批处理文件中编写的整个行应分配给循环变量eol=。因此,指定I来定义一个空的字符串定界字符列表,这将完全禁用行分割行为。

批处理文件的主要代码也可以编写如下:

I

此批处理文件的变体以静默方式运行命令/可执行文件,同时禁止标准输出并将错误输出记录到文件夹中的临时文件中,该临时文件的名称为具有扩展名delims=的批处理文件。

如果退出的任何命令/可执行文件的值与上次执行的命令/可执行文件的退出代码不等于0,或者退出带有删除空日志文件和退出代码0的标签行,则退出循环。

甚至更增强的版本是:

title Disable UAC
del "%TEMP%\%~n0.log" 2>nul
for /F "useback skip=2 delims= eol=" %%I in ("%~f0") do (
    if "%%~I" == ":MainCode" goto Finished
    %%I >nul 2>>"%TEMP%\%~n0.log" || exit /B
)
:Finished
del "%TEMP%\%~n0.log" 2>nul
exit /B 0

每个命令行开头都有一个字符,可以在 FOR 循环中执行,以控制如何执行此命令行。该字符与命令行以一个或多个空格或水平制表符分隔。执行控制字符的含义如下:

  • c ...使用命令 CALL 调用此命令行,以在执行之前扩展环境变量引用,并且不抑制任何输出,也不评估退出代码。
  • e ... ...在不评估任何输出且不评估退出代码的情况下执行此命令行。
  • p ... ...此行仅包含要使用命令 ECHO 打印的文本(进入控制台窗口)。
  • r ...运行.log时,禁止显示标准输出,将错误记录输出到日志文件,并退出批处理文件(在命令/可执行程序的退出代码上执行),运行此命令行表示执行不成功。

@echo off goto MainCode e TITLE Disable UAC c echo Errors are log into file: "%TEMP%\%~n0.log" p STEP 1: Disable User Account Control r reg.exe add HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f r reg.exe add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f :MainCode del "%TEMP%\%~n0.log" 2>nul for /F "useback skip=2 tokens=1* eol=" %%I in ("%~f0") do ( if %%I == :MainCode del "%TEMP%\%~n0.log" 2>nul & exit /B 0 if %%I == c ( call %%J ) else if %%I == e ( %%J ) else if %%I == p ( echo(%%J ) else ( %%J >nul 2>>"%TEMP%\%~n0.log" || exit /B ) ) 0之间的(使得通过在命令块中仅插入echo的行也可以打印空白行。

FOR 选项%%J替换为p。这里需要 FOR 的行拆分行为,该行具有默认的分隔符空间和制表符。第一个用空格/制表符分隔的字符串应分配给循环变量delims=,此处是每行开头的命令执行控制字符,分别执行或输出标签tokens=1*。分配给循环变量I的第一个字符串之后,空格/制表符之后的所有内容都不应在空格/制表符中进一步分割。因此,:MainCode后面附加了I,它告诉 FOR *tokens=1之后的空格/制表符之后分配其余行,ce,然后根据ASCII table(即字符p)进入下一个循环变量。

要了解所使用的命令及其工作方式,请打开命令提示符窗口,在其中执行以下命令,并非常仔细地阅读每个命令显示的所有帮助页面。

  • r ...解释J ...参数0的完整文件名,即具有文件扩展名和完整路径的批处理文件名以及call /?,其扩展为批处理文件的文件名,不带文件扩展名和路径。
  • %~f0
  • %~n0
  • echo /?
  • exit /?
  • for /? ...解释说goto /?表示if /?更大还是等于 if ERRORLEVEL number不等于 ERRORLEVEL,许多没有阅读帮助/文档的人都认为。
  • number
  • number

另请参阅single line with multiple commands using Windows batch file,以获取关于运算符reg /?reg add /?的解释。

答案 1 :(得分:0)

我会使用条件执行来做到这一点,例如:

@echo off
CALL :DISABLEUAC || GOTO :ERRHANDLER
GOTO :EOF

:DISABLEUAC
ECHO STEP 1: Disable User Account Control
reg add HKEY_CURRENT_USER\... /f || GOTO :EOF
reg add HKEY_LOCAL_MACHINE\... /f || GOTO :EOF
GOTO :EOF

:ERRHANDLER
ECHO An error was found w/ error code of: %ERRORLEVEL%
GOTO :EOF
  • 在DISABLEUAC子例程中,如果命令失败,请使用条件执行退出该子例程

  • 在主模块中,如果子例程失败,则调用子例程并使用条件执行跳转到错误处理程序。