【问题标题】:Batch file and DEL errorlevel 0 issue批处理文件和 DEL 错误级别 0 问题
【发布时间】:2021-07-09 23:32:50
【问题描述】:

批处理必须从特定位置删除文件和目录,并将成功或 stdout/stderr 消息输出到新的 .txt 文件。我已经创建了大部分脚本,它完全按照它应该执行的方式执行,除非删除成功时它会前进到下一行,而不是在日志上回显“成功”消息。

echo Basic Deletion Batch Script > results.txt
@echo off
call :filelog >> results.txt 2>&1
notepad results.txt
exit /b

:filelog

call :delete new.txt
call :delete newer.txt
call :delete newest.txt
call :remove c:\NoSuchDirectory

GOTO :EOF

:delete
echo deleting %1
del /f /q c:\Users\newuser\Desktop\%1 
if errorlevel 0 echo succesful

GOTO :EOF

:remove
echo deleting directory %1
rmdir /q /s %1

GOTO :EOF

由于某种原因,我找不到 if del 成功 echo 'successful' 的语法。在上面的示例中,如果我删除该行

if errorlevel 0 echo successful

一切正常,但没有成功消息。留下这条线,它呼应了每条线的成功。

【问题讨论】:

    标签: batch-file cmd echo delete-file errorlevel


    【解决方案1】:

    delErrorLevel?

    只要给定的参数有效,del 命令就不会设置ErrorLevel,它甚至会在这种情况下将ErrorLevel 重置为0(至少对于Windows 7)。

    del 仅在提供无效开关的情况下修改ErrorLeveldel /XErrorLevel 设置为1),根本没有指定任何参数(delErrorLevel 设置为@987654336 @ too),或者给出了不正确的文件路径(del :ErrorLevel 设置为 123),至少对于 Windows 7。

    可能的解决方法

    一种可能的解决方法是捕获delSTDERR 输出,因为在删除错误的情况下,相关消息(Could Not Find [...]Access is denied.The process cannot access the file because it is being used by another process.)会写入那里。这可能看起来像:

    for /F "tokens=*" %%# in ('del /F /Q "\path\to\the\file_s.txt" 2^>^&1 1^> nul') do (2> nul set =)
    

    要直接在命令提示符中而不是在批处理文件中使用代码,请编写 %# 而不是 %%#

    如果您不想删除只读文件,请从 del 命令行中删除 /F
    如果您确实需要提示(如果文件路径中存在通配符? 和/或*),请删除/Q

    代码说明

    这将执行命令行del /F /Q "\path\to\the\file_s.txt"。通过2>&1 1> nul 部分,STDOUT 的命令输出将被解除,其STDERR 输出将被重定向,以便for /F 接收它。

    如果删除成功,del 不会生成STDERR 输出,因此for /F 循环不会迭代,因为没有要解析的内容。注意ErrorLevel 在这种情况下不会被重置,它的值保持不变。

    如果for /Fdel命令行接收到任何STDERR输出,则执行循环体中的命令,即set =;这是一个无效的语法,因此setErrorLevel 设置为12> nul 部分避免显示消息 The syntax of the command is incorrect.

    要显式设置ErrorLevel,您还可以使用cmd /C exit /B 1。也许这条线更清晰。当然它更灵活,因为您可以声明任何(带符号的 32 位)数字,包括 0 来清除它(省略数字也会清除它)。不过在性能方面可能会差一些。

    应用示例

    以下批处理文件演示了如何应用上述解决方法:

    :DELETE
    echo Deleting "%~1"...
    rem this line resets ErrorLevel initially:
    cmd /C exit /B
    rem this line constitutes the work-around:
    for /F "tokens=*" %%# in ('del /F /Q "C:\Users\newuser\Desktop\%~1" 2^>^&1 1^> nul') do (2> nul set =)
    rem this is the corrected ErrorLevel query:
    if not ErrorLevel 1 echo Deleted "%~1" succesfully.
    goto :EOF
    

    预设ErrorLevel

    除了上面提到的命令cmd /C exit /B,你还可以使用> nul ver来重置ErrorLevel。这可以与for /F 循环解决方法结合使用,如下所示:

    > nul ver & for /F "tokens=*" %%# in ('del /F /Q "\path\to\the\file_s.txt" 2^>^&1 1^> nul') do (2> nul set =)
    

    没有for /F 的替代方法

    除了使用for /F 来捕获delSTDERR 输出之外,还可以使用find 命令,如find /V "",如果出现空字符串,则返回ErrorLevel1在和0 否则:

    del "\path\to\the\file_s.ext" 2>&1 1> nul | find /V "" 1> nul 2>&1
    

    但是,如果删除成功,这将返回 ErrorLevel1,否则返回 0。为了扭转这种行为,可以像这样附加一个if/else 子句:

    del "\path\to\the\file_s.ext" 2>&1 1> nul | find /V "" 1> nul 2>&1 & if ErrorLevel 1 (1> nul ver) else (2> nul set =)
    

    不同的方法:在del 之后检查文件是否存在

    一种完全不同的方法是在尝试删除文件后检查文件是否存在(感谢用户Sasha 提供hint!),例如:

    del /F /Q "\path\to\the\file_s.txt" 1> nul 2>&1
    if exist "\path\to\the\file_s.txt" (2> nul set =) else (1> nul ver)
    

    【讨论】:

    • 为什么不检查删除尝试后文件是否仍然存在?
    • 当然,@Sasha,这当然也是可能的;如果需要,您可以编写自己的答案来说明如何完成;还是你更喜欢我扩展我的?
    • 恕我直言,最好有一个全面的答案。所以如果你能扩展你的那将是完美的。
    • 2>&1 1> nul 可能无法达到您的预期;应该是1>nul 2>&12>&1 1>nul 将 STDERR 重定向到 STDOUT,将 STDOUT 中的内容重定向到 NUL,即仅将 STDERR 输出到控制台。
    • 要交换标准输出和标准错误,可以使用3>&2 2>&1 1>&3(即使在 bash/sh 中)。所以查找方法可能是del /s /q "%~1" 3>&2 2>&1 1>&3 | find /V "" 3>&2 2>&1 1>&3 & if errorlevel 1 (1> nul ver) else (2> nul set =)
    【解决方案2】:

    当使用这个语法时,而不是这个

    if errorlevel 0 echo successful
    

    您可以使用它 - 因为错误级别 0 始终为真。

    if not errorlevel 1 echo successful
    

    【讨论】:

      【解决方案3】:

      只需使用UnxUtils 中的rm(或gowcygwin)。如果文件不存在或删除文件时出现任何错误,它会正确设置错误级别。

      【讨论】:

      • 确认,rm 在不存在的文件上设置错误级别,而del 没有。
      【解决方案4】:

      这是原始提问者作为编辑添加的,我已将其转换为社区 wiki 答案,因为它应该是答案,而不是编辑。

      我发现了如何做到这一点......无论如何都是一种方式。

      echo Startup > results.txt
      @echo off
      call :filelog >> results.txt 2>&1
      notepad results.txt
      exit /b
      
      :filelog
      
      call :delete new.txt
      call :delete newer.txt
      call :delete newest.txt
      call :remove c:\NoSuchDirectory
      
      GOTO :EOF
      
      :delete
      echo deleting %1
      dir c:\users\newuser\Desktop\%1  >NUL 2>&1
      SET existed=%ERRORLEVEL%
      del /f /q c:\Users\newuser\Desktop\%1 
      dir c:\users\newuser\Desktop\%1 2>NUL >NUL
      if %existed% == 0 (if %ERRORLEVEL% == 1  echo "successful" )
      
      GOTO :EOF
      
      :remove
      echo deleting directory %1
      rmdir /q /s %1
      
      GOTO :EOF
      

      【讨论】:

        【解决方案5】:

        IF ERRORLEVEL 0 [cmd] 每次都会执行,因为 IF ERRORLEVEL # 检查 ERRORLEVEL 的值是否大于或等于 #。因此,每一个错误代码都会导致[cmd]的执行。

        一个很好的参考是:http://www.robvanderwoude.com/errorlevel.php

        >IF /?
        Performs conditional processing in batch programs.
        
        IF [NOT] ERRORLEVEL number command
        IF [NOT] string1==string2 command
        IF [NOT] EXIST filename command
        
          NOT               Specifies that Windows should carry out
                            the command only if the condition is false.
        
          ERRORLEVEL number Specifies a true condition if the last program run
                            returned an exit code equal to or greater than the number
                            specified.
        

        我建议将您的代码修改为以下内容:

        :delete
        echo deleting %1
        del /f /q c:\Users\newuser\Desktop\%1 
        if errorlevel 1 (
            rem This block executes if ERRORLEVEL is a non-zero
            echo failed
        ) else (
            echo succesful
        )
        
        GOTO :EOF
        

        如果你需要处理多个 ERRORLEVEL 的东西,你可以这样做:

        :delete
        echo deleting %1
        del /f /q c:\Users\newuser\Desktop\%1 
        if errorlevel 3 echo Cannot find path& GOTO :delete_errorcheck_done
        if errorlevel 2 echo Cannot find file& GOTO :delete_errorcheck_done
        if errorlevel 1 echo Unknown error& GOTO :delete_errorcheck_done
        echo succesful
        :delete_errorcheck_done
        
        GOTO :EOF
        

        :delete
        echo deleting %1
        del /f /q c:\Users\newuser\Desktop\%1 
        goto :delete_error%ERRORLEVEL% || goto :delete_errorOTHER
        
        :delete_errorOTHER
        echo Unknown error: %ERRORLEVEL%
        GOTO :delete_errorcheck_done
        :delete_error3
        echo Cannot find path
        GOTO :delete_errorcheck_done
        :delete_error2
        echo Cannot find file
        GOTO :delete_errorcheck_done
        :delete_error0
        echo succesful
        :delete_errorcheck_done
        
        GOTO :EOF
        

        【讨论】:

          【解决方案6】:

          aschipfl 的答案很棒(谢谢,帮了我很多!)使用 Presetting ErrorLevel 下的代码你会得到一个很好的标准函数:

          注意在del 语句中使用%~1 而不是%1,否则如果使用带引号的文件名会出错。

          ::######################################################################
          ::call :DELETE "file.txt"
          ::call :DELETE "file.txt" "error message"
          :DELETE
            >nul ver && for /F "tokens=*" %%# in ('del /F /Q "%~1" 2^>^&1 1^> nul') do (2>nul set =) || (
              if NOT .%2==. echo %~2
            )
          goto :EOF
          

          顺便说一句:您可以提供一个漂亮的错误消息作为第二个参数
          BTW 2:在 cmets 中使用 :: 而不是 REM 会使代码更具可读性。

          【讨论】:

            【解决方案7】:

            代码:

            错误代码: (你做了什么)

            if errorlevel 0 echo succesful
            

            这里的问题是您没有将 errorlevel 作为变量调用,而且您也没有将运算符添加到语句中。

            正确代码: (实际上应该是这样的。)

            if %ERRORLEVEL% EQU 0 echo succesful
            

            定义:

            EQU: EQU 代表相等。这种运算符也称为关系运算符。这是documentation操作员的链接,如果您想了解更多信息,还有其他的,但这对我有帮助。


            ERRORLEVEL: 被声明为变量,通常获取最后一个命令运行的错误级别。变量通常在像这样的百分号之间时被调用

            %foo%
            

            有关变量的更多帮助,请转到 cmd(您可以通过在 Windows 10 上搜索它来访问)并输入“set /?”,不带引号。 set 命令是用来设置变量的命令

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-01-26
              • 1970-01-01
              • 2021-02-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-01-21
              相关资源
              最近更新 更多