Пакетный файл и проблема DEL errorlevel 0

Пакет должен удалять файлы и каталоги из определенных местоположений и выводить сообщения об успешном выполнении или стандартные сообщения 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

По какой-то причине я не могу найти синтаксис, если del завершается успешно, эхом «успешно». В приведенном выше примере, если я удалю строку

if errorlevel 0 echo successful

Все работает нормально, но нет сообщения об успехе. С этой оставшейся строкой он повторяет успех для каждой строки.


person John Scott    schedule 09.04.2014    source источник


Ответы (7)


del и ErrorLevel?

Команда del не устанавливает ErrorLevel до тех пор, пока допустимы заданные аргументы, в таких случаях она даже сбрасывает ErrorLevel на 0 (по крайней мере, для Windows 7).

del изменяет ErrorLevel только в том случае, если указан недопустимый переключатель (del /X устанавливает ErrorLevel в 1), аргументы вообще не указаны (del также устанавливает ErrorLevel в 1) или указан неверный путь к файлу (del : устанавливает ErrorLevel в 123), по крайней мере для Windows 7.

Возможный обходной путь

Возможный обходной путь — захватить вывод STDERR del, потому что в случае ошибок удаления соответствующие сообщения (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 =)

Чтобы использовать код непосредственно в командной строке, а не в пакетном файле, напишите %# вместо %%#.

Если вы не хотите удалять файлы только для чтения, удалите /F из командной строки del;
если вам нужны подсказки (если в пути к файлу присутствуют подстановочные знаки ? и/или *), удалите /Q.

Объяснение кода

Это выполняет командную строку del /F /Q "\path\to\the\file_s.txt". По части 2>&1 1> nul вывод команды на STDOUT будет отклонен, а его вывод STDERR будет перенаправлен так, чтобы for /F принял его.

Если удаление прошло успешно, del не генерирует вывод STDERR, следовательно, цикл for /F не повторяется, поскольку анализировать нечего. Обратите внимание, что ErrorLevel в этом случае не сбрасывается, его значение остается неизменным.

Если for /F получает любой вывод STDERR из командной строки del, выполняется команда в теле цикла, то есть set =; это недопустимый синтаксис, поэтому set устанавливает ErrorLevel в 1. Часть 2> 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 для захвата STDERR вывода del команду find можно также использовать как find /V "", которая возвращает ErrorLevel из 1, если приходит пустая строка, и 0 в противном случае:

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

Однако это вернет ErrorLevel из 1 в случае успешного удаления и 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 за подсказку!), например, так :

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)
person aschipfl    schedule 28.10.2015
comment
Почему бы просто не проверить, существует ли файл после попытки удаления? - person Sasha; 11.07.2019
comment
Конечно, @Sasha, это, конечно, тоже возможно; вы можете написать свой собственный ответ, чтобы показать, как это можно сделать, если хотите; или вы предпочитаете, чтобы вместо этого я продлил свой? - person aschipfl; 11.07.2019
comment
ИМХО лучше один исчерпывающий ответ. Так что, если бы вы могли расширить свой, это было бы прекрасно. - person Sasha; 11.07.2019
comment
2>&1 1> nul может не достичь того, чего вы ожидали; должно быть 1>nul 2>&1. 2>&1 1>nul перенаправляет STDERR в STDOUT, а то, что должно было быть в STDOUT, в NUL, т.е. только выводит STDERR на консоль. - person lxvs; 02.05.2021
comment
@lxvs, я намеренно использовал выражение 2>&1 1> nul, потому что хочу захватить STDERR с помощью for /F и игнорировать STDOUT. Выражение 1> nul 2>&1 подавляло бы как STDOUT, так и STDERR, так что нечего было бы захватывать… - person aschipfl; 02.05.2021

При использовании этого синтаксиса вместо этого

if errorlevel 0 echo successful

вы можете использовать это, потому что уровень ошибки 0 всегда истинен.

if not errorlevel 1 echo successful
person foxidrive    schedule 09.04.2014

Просто используйте rm из UnxUtils (или gow или cygwin). Он правильно устанавливает уровень ошибки в случае несуществующего файла или любых ошибок при удалении файла.

person clueless    schedule 15.10.2018
comment
Подтвердите, rm устанавливает уровень ошибки для несуществующего файла, а del — нет. - person Bob Stein; 08.04.2019

Это было добавлено как редактирование первоначальным автором вопроса, я преобразовал его в ответ вики сообщества, потому что это должен быть ответ, а не редактирование.

Я узнал, как это сделать... во всяком случае, одним способом.

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
person Community    schedule 24.06.2015

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

OR

: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
person Hossy    schedule 22.09.2015

Ответ aschipfl великолепен (спасибо, мне очень помог!), используя код в разделе Presetting ErrorLevel, вы получаете хорошую стандартную функцию:

Позаботьтесь об использовании %~1 вместо %1 в операторе del, иначе вы получите ошибки, если будете использовать имя файла в кавычках.

::######################################################################
::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

Кстати 1: Вы можете указать отличное сообщение об ошибке в качестве второго параметра
Кстати 2: Использование :: вместо REM для комментариев делает код еще более читабельным.

person CONSULitAS    schedule 30.03.2017

Код:

Код ошибки: (Что вы сделали)

if errorlevel 0 echo succesful

Проблема здесь в том, что вы не вызываете errorlevel как переменную, и, кроме того, вы также не добавили оператор в оператор.

Правильный код: (Вот каким он должен быть на самом деле.)

if %ERRORLEVEL% EQU 0 echo succesful

Определения:

EQU: EQU означает равный. Этот тип оператора также называется оператором отношения. Вот документация по операторам, если вы хотите узнать больше, есть и другие, но это помогло мне.


ERRORLEVEL: объявляется как переменная и обычно получает уровень ошибки последней выполненной команды. Переменные обычно вызываются, когда они находятся между знаками процента, как это

%foo%

Для получения дополнительной помощи по переменным перейдите к cmd (к которому вы можете перейти, выполнив поиск в Windows 10) и введите set /? без кавычек. команда set — это команда, которую вы используете для установки переменных

person Jonathan J. Pecany    schedule 21.06.2020