错误检测和记录 - 批处理文件

时间:2017-02-02 14:56:52

标签: windows batch-file

我正在编写一个批处理脚本来从服务器中提取数据。如果以下脚本中的任何命令失败,我想退出批处理程序并将错误记录在错误日志文件中。

这是我到目前为止所做的:

call epmautomate login aaaa@a.com abcd123 https://www.website.com
epmautomate exportdata ABC_DATA_EXTRACT & epmautomate downloadfile ABC_DATA_EXTRACT.zip & MOVE "C:\doc\bin\ABC_DATA_EXTRACT.zip" C:\Users\Administrator\Documents\folder & cd C:\Users\Administrator\Documents\folder & unzip -n C:\Users\Administrator\Documents\folder\ABC_DATA_EXTRACT.zip & C:\Users\Administrator\Documents\folder\change_header.cmd

如何添加错误检测和日志记录?

1 个答案:

答案 0 :(得分:0)

在我讨论日志记录和错误之前,我想指出我们希望您清理代码的原因(您没有像我们的建议那样做):

  • 没有理由在命令之间使用&。将每个命令放在单独的行上。 &表示"无论发生什么,也执行以下"但是使脚本不可读。因此,如果可能的话,总是将每个命令放在单独的行上。

  • 您使用不同的方式执行epmautomate。如果是脚本,则每次要执行时都使用call,否则当您希望执行call时,根本不要使用epmautomate。假设您的代码有效,我可以假设它不是脚本,并且call不是必需的。

  • 最好用双qoutes包围所有你的路径

现在记录和错误检测 我知道两种不同的方法来记录错误并在错误上退出批处理文件。最重要的条件是您在脚本中使用的所有命令(个人,内置或第三方脚本/软件)必须正确设置errorlevel 。如果您希望cmd解释器知道在执行命令期间发生错误,那么这是唯一的条件。这次适当的日志文件的另一个条件是您使用的命令应该写出正确的错误消息。在这两种方法中,使用2>> error redirection operator将写入错误通道的错误消息附加到日志文件中(该链接还显示了如果感兴趣,如何重定向输出和错误消息)。我假设logfile变量中提供了不带双引号的日志文件路径。我每次使用变量时都添加了双引号:"%logfile%"

  1. 第一种方法使用IF statement IF ERRORLEVEL n 会检查错误级别是否大于或等于n 。因此,如果我们假设命令command1正确设置了错误级别(如果成功则为0,否则为1或更大),如果在执行期间发生错误级别,则以下应该能够停止脚本

    command1 2>> "%logfile%"
    IF ERRORLEVEL 1 (
        REM some extra personal errormessage if needed
        echo command1 failed, check the log-file for more info
        REM Exit current script and set errorlevel on 1 (failure)
        exit /b 1
    )
    

    如果需要,可以使用其他严格正整数退出并使用个人错误代码。

  2. 第二种方法在代码块(命令组)中使用conditional execution执行。仅当command1 && command2成功时,command2才会执行command1command1 && command2之后的错误级别将显示两者是否已成功退出,或者其中一个是否已退出并显示错误。如果您在( )之间将命令组合在一起,请执行以下操作:

    (
        command1
        command2
        command3
    )
    

    cmd解释器只会将所有命令放在一行上并将&&置于其间:它将最终执行command1 && command2 && command3。因此,要执行一组命令并在执行其中一个命令期间发生错误时退出,可以使用

    (
        command1
        command2
        command3
    ) 2>> "%logfile%"
    IF ERRORLEVEL 1 (
        REM some extra personal errormessage if needed
        echo An error occured, consult the log-file for more info
        REM Exit current script and set errorlevel on 1 (failure)
        exit /b 1
    )
    

    这种方法有一个缺点:在代码块中使用变量是有限的。由于块内的所有命令都被解析并作为单个命令执行,因此您无法在块内为变量赋予新值,并在具有正常变量扩展的同一块内使用该新值(即%var%)。如果您希望能够使用新值,则必须使用delayed expansion

  3. 您选择哪种方法取决于具体情况以及您希望实现的目标。第一种方法是更普遍的方法。感谢"高"各种IF statements,它可用于不设置错误级别但使用另一种方式传递执行期间发生的错误的命令。第一种方法还允许更准确的错误分析,因为您知道哪个命令导致了错误,并且可以轻松地在日志文件中添加该信息。但是有一个问题:你会有一些严肃的类型工作。您可以尝试使用执行所有命令的函数和exit the batch script from within the function来解决该问题,如果需要,但这并不容易。在使用该功能时,我还有一个更简单的选择,即放弃脚本,但我稍后会再回过头来看。 第二种方法的缺点是您无法轻易识别发生错误的命令。你可以通过打印成功"来解决这个问题。每个命令到日志文件后的消息。毕竟,一个好的日志文件也应包含已成功执行的内容。

    @echo off
    set logfile=C:\Users\path\to\logfile errors.txt
    (    
        epmautomate login aaaa@a.com abcd123 https://www.website.com 2>> "%logfile%"
        echo first epmautomate ok >> "%logfile%"
        epmautomate exportdata ABC_DATA_EXTRACT 2>> "%logfile%"
        echo second epmautomate ok >> "%logfile%"
        epmautomate downloadfile ABC_DATA_EXTRACT.zip 2>> "%logfile%"
        echo third epmautomate ok >> "%logfile%"
        MOVE "C:\doc\bin\ABC_DATA_EXTRACT.zip" "C:\Users\Administrator\Documents\folder" 2>> "%logfile%"
        echo move zip ok >> "%logfile%"
        cd "C:\Users\Administrator\Documents\folder" 2>> "%logfile%"
        echo cd to admin folder ok >> "%logfile%"
        unzip -n "C:\Users\Administrator\Documents\folder\ABC_DATA_EXTRACT.zip" 2>> "%logfile%"
        echo unzip zipfike ok >> "%logfile%"
        call "C:\Users\Administrator\Documents\folder\change_header.cmd" 2>> "%logfile%"
        echo call to change-header ok >> "%logfile%"
    )
    IF ERRORLEVEL 1 (
        REM some extra personal errormessage if needed
        echo An error occured, consult the log-file for more info
        REM Exit current script and set errorlevel on 1 (failure)
        exit /b 1
    )
    

    它完成了这项工作。您只需在日志中编写所需的每个内容,并在代码块中移动错误重定向。如果发生错误,您仍然需要回顾代码以了解哪个命令在您的日志文件中产生了最后的错误消息,但至少您知道哪个命令在您的代码块中失败了谢谢成功的消息。

    为了完整性,我将使用函数来添加第一种方法的解决方案,如前所述(我实际上更喜欢)执行命令。但由于exiting a script from a function可能相当复杂,我宁愿使用第二种方法(代码块中的条件执行)的想法放弃执行其余命令:

    @echo off
    set logfile=C:\Users\path\to\logfile_errors.txt
    
    (    
        call :executeOwn epmautomate login aaaa@a.com abcd123 https://www.website.com
        call :executeOwn epmautomate exportdata ABC_DATA_EXTRACT
        call :executeOwn epmautomate downloadfile ABC_DATA_EXTRACT.zip
        call :executeOwn MOVE "C:\doc\bin\ABC_DATA_EXTRACT.zip" "C:\Users\Administrator\Documents\folder"
        call :executeOwn cd "C:\Users\Administrator\Documents\folder"
        call :executeOwn unzip -n "C:\Users\Administrator\Documents\folder\ABC_DATA_EXTRACT.zip"
        call :executeOwn call "C:\Users\Administrator\Documents\folder\change_header.cmd" 
    ) 
    
    REM Exit current batch script with error status from last executed call
    exit /b %ERRORLEVEL%
    
    :executeOwn
    %* 2>> "%logfile%"
    IF ERRORLEVEL 1 (
        REM Write command that was executing to log-file
        echo FAILED : [ %* ] >> "%logfile%"
        REM some extra personal errormessage if needed
        echo An error occured, consult the log-file for more info
        REM Exit current script and set errorlevel on 1 (failure)
        exit /b 1
    )
    echo succeeded : [ %* ] >> "%logfile%"
    exit /b 0
    

    这种方法甚至可以通过不需要延迟扩展的方式解决变量扩展问题。您只需使用%%var%%代替通常的%var%来扩展代码块中的变量,:executeOwn就可以将其扩展为最新值。 注意:代码块中的^&<>等特殊字符必须使用插入符^^^&^<^>进行转义才能在:executeOwn函数中执行,除非是它们是双引号字符串的一部分。