Почему не работает errorlevel в цикле
Перейти к содержимому

Почему не работает errorlevel в цикле

  • автор:

Какие задачи не решаются bat-файлами?

Бат-файлы ведут свою историю со времен MS-DOS. Новые фичи добавлялись с сохранением обратной совместимости. Из-за этого многое в языке bat-файлов, как мы увидим далее, нелогично и неудобно.

Вместе с тем, в bat-файле можно использовать переменные, условия, циклы и подпрограммы. При помощи некоторых костылей можно передавать данные на вход команд и разбирать их вывод. Проще говоря, можно программировать.

В этой статье мы постараемся дать обзор основных элементов языка командного интерпретатора Windows, с помощью которых можно реализовать любой алгоритм.

Зачем же сегодня использовать этот язык? Главным образом, для автоматизации выполнения консольных команд. Запуск команд выполняется в bat-файле проще, чем в каком-либо другом языке: инструкция запуска команды – это сама команда без каких бы то ни было ключевых слов, знаков препинания и подключаемых библиотек.

Для сравнения, Java, .NET и Python для выполнения этой простой задачи требуют подключить библиотеку/сборку/модуль и вызвать метод.

Язык C++ сам по себе вообще не определяет для этого стандартный и независимый от платформы способ. Тут вам понадобится библиотека целевой операционной системы. В Windows это windows.h и функция CreateProcess() , а в Linux надо вызвать fork() , а затем exec() .

Второе место по простоте вызова команд операционной системы занимает Perl – в нем достаточно заключить команду в обратные кавычки ( « ).

Первое место с bat-файлами делят PowerShell и bash.

Зачем же разбирать командный интерпретатор, а не Power Shell, который, кроме всего прочего, дает доступ ко всему богатству возможностей .NET Framework?

Прежде всего, ради искусства. Разные люди делают интересные вещи чисто на bat-файлах именно потому, что это сложно. Т.е. можно рассматривать язык командного интерпретатора как эзотерический язык программирования.

Кроме того, как-то раз в реальном проекте мне пришлось поддерживать bat-скрипты. Так что рано их списывать со счетов как морально устаревшую технологию.

Еще одно полезное отличительное свойство командного интерпретатора – для получения справки не нужен ни Гугл, ни интернет. Нужно лишь набрать команду help . Причем, информация русском языке, если у вас русская винда.

Переменные

Переменные в bat-файлах – это переменные окружения. Это значит, что все переменные, установленные скриптом, доступны процессам, которые он запускает. Это надо иметь ввиду при работе с паролями.

Переменные создаются и изменяются командой set :

Создали переменную с именем hello и значением world.

Команда set без параметров выводит список всех переменных.

Следующая строка выведет слово «world»:

Имена переменных нечувствительны к регистру. Следующая строка тоже выведет слово «world»:

Значения переменных могут содержать знак равенства:

set equation=x=y echo %equation%

Значение переменных вставляются в текст команды, и только потом происходит ее разбор. Это значит, что переменная может содержать саму команду и/или любое количество параметров.

Специальные переменные

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

%0 – имя bat-файла, который сейчас выполняется, в том виде, как оно указано в командной строке. Для запуска bat-файла его имя может быть написано с полным путем, с относительным путем, без пути, с расширением, без расширения, маленькими или заглавными буквами. Соответственно, в переменной %0 оно может оказаться во всех этих вариантах. Но в любом случае командой %0 скрипт запускает сам себя.

%1, %2%9 – параметры. Командная строка разделяется на токены по пробелам. При этом каждая часть, заключенная в кавычки, считается одним токеном. Кавычки, если есть, тоже попадают в эти переменные.

%* – все параметры, т.е. всё, что идет в командной строке после имени bat-файла, со всеми кавычками и пробелами, какие есть.

%errorlevel% — код возврата последней команды

%CD% — текущая директория

%DATE% и %TIME% — текущие дата и время

%RANDOM% — случайное число

Преобразование параметров при подстановке

%~1 – первый параметр без кавычек. Как вы помните, параметр может быть указан либо в кавычках, либо без них, и кавычки, если они есть, будут и в переменной. От того, есть ли кавычки в переменной, зависит, будет ли содержащее ее выражение синтаксически правильным. Проверить наличие кавычек с помощью оператора if не получится, потому что в одном из двух случаев выполнение скрипта прервется из-за синтаксической ошибки. Однако, тильда перед именем переменной гарантирует, что кавычек на месте этой переменной не будет.

%~dp0 – полный путь к папке текущего bat-файла, без имени самого файла.

%~nx0 – имя и расширение текущего bat-файла, без пути

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

Цикл FOR

Это единственный вид цикла. Если нужно что-то типа while или until, то придется использовать операторы if и goto.

Зато цикл FOR имеет несколько опций, выходящих за рамки того, что обычно делает цикл

Итерирование по словам

@echo off FOR %%a in (one two three) do echo %%a

Выводит каждое слово в новой строке:

one two three

@echo off отключает вывод команд. @ перед командой отключает вывод данной команды

Этот пример необходимо записать в bat-файл и запустить файл. Если вводить цикл в командной строке, то он должен выглядеть слегка иначе:

FOR %a in (one two three) do @echo %a

Да, синтаксис переменной цикла зависит от того, вводим ли мы этот цикл в командной строке или он записан в файл. Если какой-то софт запускает команду, которую вы ему укажете, и вы хотите использовать в ней цикл, синтаксис зависит от того, каким образом этот софт передает команду командному интерпретатору. Это можно делать через командную строку или через стандартный ввод. В этих случаях используется один знак процента. Вариант с файлом мы уже пробовали в примере. Там используются два знака процента.

Цикл разбирает строку в скобках по тем же правилам, что параметры командной строки: строка в кавычках – это один параметр. Пример:

@echo off FOR %%a in ("twenty one" "twenty two") do echo %%~a

Выводит две строки:

twenty one twenty two

Строки выводятся без кавычек, потому что использована переменная %%~a. %%a содержит кавычки.

Цикл по файлам или папкам

Следующая команда выводит список файлов в текущем каталоге:

FOR %%a in (*.*) do echo %%a

А так можно получить список папок:

FOR /D %%a in (*.*) do echo %%a

Параметр /R включает обход всех подкаталогов. При этом в переменной цикла полные пути:

FOR /R %%a in (*.*) do echo %%a

FOR /D /R %%a in (*.*) do echo %%a

Вместо » *.* » можно писать просто » * «. Результат абсолютно такой же: в обоих случаях выбираются файлы как с расширениями, так и без.

Цикл по числам с заданным шагом

Используем параметр /L, а в скобках указываем начало, шаг и конец (включительно) через запятую. Обратите внимание, что для числа, указанного как конец интервала, команда тоже будет вызвана, если длина интервала делится на шаг. Шаг может быть отрицательным.

FOR /L %%a in (10,-1,5) do @echo %%a

10 9 8 7 6 5

Разбор файлов и строк

FOR /F %%a in (список.txt список2.txt) do echo %%a

Каждый файл открывается, каждая строка читается и разбивается на одну или более подстрок. Подстроки записываются в отдельные переменные. Например, если переменная цикла — %%a, то будут созданы переменные %%a, %%b, %%c и т.д.

При разборе строки разделителями по умолчанию считаются пробелы и табуляции.

Вот так можно разобрать строку, указанную непосредственно:

FOR /F %%a in (“строка”) do echo %%a

А так разбирается вывод команды. Это вообще единственный способ получить вывод команды в переменную. То есть, даже если вывод состоит из одной строки, и ее не надо разбирать, всё равно приходится писать цикл.

FOR /F %%a (‘dir /B’) do echo %%a

Выводит список файлов, разбирая вывод команды dir .

Наберите help for , чтобы узнать все опции.

Вызов подпрограмм и других скриптов

Очередная контринтуитивная фича bat-файлов в том, что, если из одного bat-файла вызвать другой, выполнение первого файла не продолжится, если только не использовать ключевое слово call. То есть, мы должны разбираться, чем является команда, которую мы хотим вызвать. Если это bat-файл, то надо перед его именем написать call. Если это exe-файл или встроенная команда, то это слово не нужно. Кроме того, если вызываемый скрипт использует команду exit , то выполнение также не вернется к нам. Чтобы завершить только текущий скрипт, надо писать exit /B .

При вызове одного скрипта из другого не создается новый процесс. Поэтому переменные окружения, установленные вызванным скриптом, видны вызывающему.

В следующем примере a.bat вызывает b.bat . Этот пример также демонстрирует работу с кодом завершения процесса.

@echo off echo Before call call b.bat echo b.bat returned %errorlevel%
echo Inside b.bat exit /B 1
Before call Inside b.bat b.bat returned 1

Командой call также можно вызвать код, находящийся в том же файле:

@echo off echo Before call call :b echo b returned %errorlevel% goto :eof :b echo Inside b.bat exit /B 1

Работает аналогично предыдущему примеру, но это один файл.

С двоеточия начинаются метки. На них можно переходить командами call и goto . В языке bat-файлов нет понятия подпрограммы. Мы будем так называть код, который вызывается через call и возвращает управление через exit /B или goto :eof . Метка :eof не определена, и это не ошибка. Эта метка не нуждается в определении. Она всегда означает конец файла. Т.е. переход на :eof – это выход – то же, что и exit /B , но без кода завершения.

Условия

Ничего не выводит.

Если строки до и после знаков равенства совпадают с учетом регистра, то команда выполняется, иначе не выполняется. В обеих строках можно использовать переменные. При этом надо иметь ввиду, что в переменных может быть всё, что угодно, включая знаки равенства и пустые строки. От значений переменных будет зависеть синтаксическая правильность команды.

Вот такой способ проверить параметр командной строки работает и при отсутствии параметра, и если в нем спецсимволы:

if «%~1″==»help» echo This is a test script && exit /B

Оператор && — это один из способов выполнить несколько команд под условием. На самом деле это ленивое «и», т.е. если первая команда вернет ненулевой errorlevel, то вторая уже не выполнится.

Другой способ – скобки:

if "%~1"=="help" ( echo This is help exit /B ) else ( Echo This is the main part )

Как видите, со скобками можно использовать слово else.

Насчет скобок нужно помнить одну вещь. Условие или цикл с блоком команд в скобках – это одна команда. Значения переменных в ней подставляются один раз в самом начале.

То есть, нельзя внутри такого блока присвоить значение переменной и позже в том же блоке его использовать. Нельзя присвоить значение на одной итерации цикла и использовать на другой.

При таких сценариях приходится выносить код в подпрограммы.

Есть еще поздняя подстановка переменных. Ее надо включать командой SETLOCAL ENABLEDELAYEDEXPANSION , а вместо символа » % » использовать » ! «.

Разыменование переменных

Отсутствие в языке массивов и словарей приходится обходить, используя переменные, имена которых содержат индексы и ключи.

Допустим, переменная A содержит ключ:

Записать значение по ключу можно так:

Следующая команда выведет «bbb»:

А вот читать по ключу не так просто. Подстановка переменных не рекурсивна. То есть, нельзя, используя только знак « % », подставить «aaa» вместо «A», а затем, «bbb» вместо «aaa».

Тут нам поможет поздняя подстановка. Она тоже не рекурсивна. Однако, используя сразу оба вида подстановок, можно выполнить замену в два этапа:

SETLOCAL ENABLEDELAYEDEXPANSION echo !%A%!

Желающие усложнить себе задачу отказом от поздней подстановки, могут разбирать вывод команды set при помощи цикла for:

for /F «tokens=1,2 delims==» %%a in (‘set %A%’) do if «%%a»==»%A%» echo %%b

Обратите внимание, что в цикле надо проверять имя переменной, которую нам вернули, даже если указать имя нужной переменной в команде set. Дело в том, что могут существовать переменные с именами, начинающимися с нужного. Если убрать параметр команды set, результат не изменится. Но указывая этот параметр, мы оптимизируем алгоритм. Но это не точно.

Арифметика

Команда set /A вычисляет выражения

SET /A A=2+3 ECHO %A%

Замена подстрок

SET A=abcdefgh ECHO %A:c=C%

Заменяет строчную букву C на заглавную. Выводит:

Выводит 2 символа, начиная с 5-го, в данном случае » ef «.

Ввод пользователя

SET /P A=Enter something:

Выведет приглашение для ввода и будет ждать ввода строки. Введенная строка окажется в переменной A.

Заключение

Язык командного интерпретатора имеет многолетнюю историю и дожил до наших дней. Он доступен на чистой винде и удобен для автоматизации выполнения команд. При этом он является языком программирования, и позволяет реализовать алгоритмы любой сложности. Самое главное в таком деле – найти подходящие инструменты командной строки, дающие доступ к нужным возможностям. Вместе с тем решение задач на bat-файлах с использованием ограниченного набора внешних команд может быть интересным упражнением.

  • Блог компании Аурига
  • Ненормальное программирование
  • Программирование
  • Оболочки
  • Разработка под Windows

AHK: ловим ошибки в цикле

Добрый день.
Просмотрел информацию о ErrorLevel, с целью продолжения цикла, когда в нем произошла ошибка.
Но необходимого не добился.
А именно, хочу добиться, когда происходит ошибка в программе, где запущен скрипт, скрипт записывал ошибку в лог (это смогу) и продолжал дальше итерации цикла.

loop < //если ошибка, записываем в log и продолжаем дальше //тут что-то происходит >

2 Ответ от ypppu 2013-10-19 12:19:20

  • ypppu
  • Разработчик
  • Неактивен

Re: AHK: ловим ошибки в цикле

После каждой команды проверяем состояние ErrorLevel. Если не равно нулю, записываем информацию об ошибке.

Антон Иваненко (Дед Мазай) пишет:

ErrorLevel

Это встроенная переменная, призванная отображать успешность или неуспешность выполнения каких-либо команд (однако, не все команды поддерживают работу с ErrorLevel). Значение ErrorLevel, равное нулю, обычно свидетельствует об удачном завершении процесса, любое другое значение, отличное от нуля — о неудаче. Значения для переменной ErrorLevel вы можете устанавливать самостоятельно.

Особо отметим, что применение переменной ErrorLevel вместе с командой RunWait позволяет нам определить успешность выполнения программы с последующим её закрытием. Большинство программ, если их работа завершена успешно, имеют код возврата, равный нулю.

Каждый поток (thread) содержит своё собственное значение ErrorLevel. При этом, если работа текущего потока прервана другим потоком, то по возобновлению работы наш текущий поток будет иметь своё собственное значение ErrorLevel, а не значение ErrorLevel прервавшего его потока.

Примечание: учитывая то, что некоторые команды выдают значение ErrorLevel большее, чем 1, наилучшим способом будет не проверять, имеет ли переменная ErrorLevel значение 1, а имеет ли переменная ErrorLevel значение, отличное от нуля.

WinWait, MyWindow, , 1 ; ищем в течение 1 секунды, существует ли окно с названием MyWindow. if ErrorLevel ; данная запись означает, что переменная ErrorLevel имеет значение, отличное от нуля. MsgBox, Окно не обнаружено. else MsgBox, Окно успешно обнаружено.

Почему не работает errorlevel в цикле

Сообщения: 53102
Благодарности: 15407

Конфигурация компьютера
Процессор: AMD Ryzen 7 7800X3D
Материнская плата: Gigabyte B650E Aorus Master
Память: Kingston Fury Renegade DDR5-6000 32GB (2×16)
HDD: Samsung SSD 850 PRO 256GB, 980 PRO 1TB
Видеокарта: Gainward GeForce RTX 3080 追风
Блок питания: be quiet! Straight Power 11 650W
Монитор: ASUS VG248QE 24″
ОС: Windows 10 Pro x64
Прочее: корпус Fractal Design Define R4
setlocal enabledelayedexpansion for /f "skip=1 tokens=1,2 delims=|" %%i in (%1) do ( for /f "delims=" %%k in (%%i.txt) do ( something.exe %%k %%i echo !errorlevel! ) )

EnableDelayedExpansion — позднее связывание.
Это сообщение посчитали полезным следующие участники:

Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети.

Сообщения: 1025
Благодарности: 180

Конфигурация компьютера

А к переменным, заключенным в !! можно применять разные :~0,-1?

——-
echo 127.0.0.1 google.ru >> %systemroot%\system32\drivers\etc\hosts && ipconfig /flushdns && echo Я ничего не трогал, оно само!

Сообщения: 27449
Благодарности: 8086

Цитата apozlevich:

А к переменным, заключенным в !! можно применять разные :~0,-1? »

Это сообщение посчитали полезным следующие участники:

Сообщения: 26
Благодарности: 1

Подниму тему.
есть листинг:

@ECHO off @COLOR 4 @CHCP 866 SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION CLS :Start For /F "Tokens=2*" %%I In ('Reg Query "HKCU\Software\Microsoft\Shared\UcClient" /V ConfigurationMode') Do SET key=%%J IF %ERRORLEVEL%==0 ( echo 1 pause ) echo 0 pause

просто ищет по реестру ключ.
ключ есть — %errorlevel%=0
ключа нет- %errorlevel%=0 но в консоли пишет, что «ошибка-мол, нет такого» значит место указано верно

как заставить реагировать %errorlevel% на наличие\отсутствие ключа в реестре?

Сообщения: 27449
Благодарности: 8086

Instant_SR, в данном случае Вы видите errorlevel от команды «for /f».

Цитата Instant_SR:

как заставить реагировать %errorlevel% на наличие\отсутствие ключа в реестре? »
@echo off setlocal enableextensions enabledelayedexpansion for /f "usebackq tokens=2*" %%i in ( `reg.exe query "HKCU\Software\Microsoft\Shared\UcClient" /v "ConfigurationMode" 2^>nul ^| find.exe /i "ConfigurationMode"` ) do set sConfigurationMode=%%j if defined sConfigurationMode ( echo ConfigurationMode: [%sConfigurationMode%]. ) else ( echo Parameter not exists. ) endlocal exit /b 0

IF – оператор условного выполнения команд в командных файлах Windows

NOT — Указывает, что Windows должна выполнить эту команду, только если условие является ложным.

ERRORLEVEL число — Условие является истинным, если код возврата последней выполненной программы не меньше указанного числа.

строка1==строка2 — Условие является истинным, если указанные строки совпадают.

EXIST имя_файла — Условие является истинным, если файл с указанным именем существует.

команда — Задает команду, выполняемую при истинности условия. За этой командой может следовать ключевое слово ELSE. В случае, если указанное условие ложно, будет выполнена команда, находящаяся после слова ELSE.

Предложение ELSE должно располагаться в той же строке, что и команда, следующая за ключевым словом IF. Например:

IF EXIST имя_файла. (
del имя_файла.
) ELSE (
echo имя_файла. missing.
)

Следующий пример содержит ОШИБКУ, поскольку команда del должна заканчиваться переходом на новую строку:

IF EXIST имя_файла. del имя_файла. ELSE echo имя_файла. Missing

Следующий пример также содержит ОШИБКУ, поскольку команда ELSE должна располагаться в той же строке, что и команда, следующая за IF:

IF EXIST имя_файла. del имя_файла.
ELSE echo имя_файла. missing
Вот правильный пример, где все команды расположены в одной строке:

IF EXIST имя_файла. (del имя_файла.) ELSE echo имя_файла. Missing

Изменение команды IF при включении расширенной обработки команд:

IF [/I] строка1 оператор_сравнения строка2 команда
IF CMDEXTVERSION число команда
IF DEFINED переменная команда

где оператор_сравнения принимает следующие значения:

EQU — равно
NEQ — не равно
LSS — меньше
LEQ — меньше или равно
GTR — больше
GEQ — больше или равно,

а ключ /I , если он указан, задает сравнение текстовых строк без учета регистра. Ключ /I можно также использовать и в форме строка1==строка2 команды IF. Сравнения проводятся по общему типу данных, так что если строки 1 и 2 содержат только цифры, то обе строки преобразуются в числа, после чего выполняется сравнение чисел.

Условие CMDEXTVERSION применяется подобно условию ERRORLEVEL, но значение сравнивается с внутренним номером версии текущей реализации расширенной обработки команд. Первая версия имеет номер 1. Номер версии будет увеличиваться на единицу при каждом добавлении существенных возможностей расширенной обработки команд. Если расширенная обработка команд отключена, условие CMDEXTVERSION никогда не бывает истинно.

Условие DEFINED применяется подобно условию EXIST , но принимает в качестве аргумента имя переменной среды и возвращает истинное значение, если эта переменная определена.

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

goto answer%ERRORLEVEL%
:answer0
echo Получен код возврата 0
:answer1
echo Получен код возврата 1

Допускается и применение описанных выше операторов числового сравнения:

IF %ERRORLEVEL% LEQ 1 goto okay

Строка %CMDCMDLINE% будет развернута в исходную командную строку, переданную CMD.EXE до любой обработки, за исключением ситуации, когда уже определена переменная среды с именем CMDCMDLINE; в подобном случае подставляется значение этой переменной.

Строка %CMDEXTVERSION% будет развернута в строку, представляющую собой текущее значение CMDEXTVERSION, за исключением ситуации, когда уже имеется переменная среды с именем CMDEXTVERSION; в подобном случае подставляется значение этой переменной.

Если параметр командной строки не задан, то пользователю выдается сообщение об ошибке.

@echo off
REM Проверить наличие имени файла, задаваемого в качестве параметра %1
REM Если параметр %1 пустой – переход на метку error
if «%1» EQU «» goto error
REM Если параметр задан, создаем пустой файл, копированием из устройства nul
copy nul «%1»
exit
:error
ECHO File name required ! Must be — %~n0 filename.ext
:exit

Примеры вывода для отвечающего и не отвечающего узлов:

Ответ от 192.168.1.1: число байт=32 время=1мс TTL=64 — если устройство с данным IP-адресом доступно;
Превышен интервал ожидания для запроса. — если устройство не отвечает;

Команда find /I «TTL» возвращает код ERRORLEVEL равный 0 , если строка «TTL» присутствует в результате выполнения ping . Ключ /I имеет смысл использовать, чтобы результат не зависил от того, строчные или заглавные символы составляют строку «ttl».
Результат работы командного файла записывается в текстовый файл iplist.txt

@ECHO OFF
REM Постоянная часть IP-адреса
set IPTMP=192.168.1.
REM Количество пингуемых узлов
set N=254
rem С какого адреса начать — начальное значение » хвоста » IP- адреса X.X.X.IPMIN
set /A IPMIN=1
ECHO %DATE% Опрос пингом %N% адресов начиная с %IPTMP%%IPMIN% >> iplist.txt
rem M0 — метка для организации цикла
:M0
rem Переменная IPFULL — полное значение текущего IP-адреса
set IPFULL=%IPTMP%%IPMIN%
rem Если » хвост «больше N – перейти к завершению работы
IF %IPMIN% GTR %N% GOTO ENDJOB
ping -n 1 %IPFULL% | find /I «TTL»
if %ERRORLEVEL%==0 Echo %IPFULL% >> iplist.txt
rem Сформируем следующий IP-адрес
set /A IPMIN=%IPMIN% + 1
rem Перейдем на выполнение следующего шага
GOTO M0
rem Завершение работы
:endjob
exit

Существуют некоторые особенности реализации командного интерпретатора CMD.EXE , которые необходимо учитывать при обработке значений переменных внутри циклов IF и FOR . Использование значений переменных внутри скобок, требует изменения стандартного режима интерпретации командного процессора. Разработчиками предусмотрена возможность запуска CMD.EXE с параметром /V:ON , что включает разрешение отложенного расширения переменных среды с применением символа восклицательного знака ( ! ) в качестве разделителя. То есть, параметр /V:ON разрешает использовать !var! в качестве значения переменной var во время выполнения внутри циклов команд FOR и IF . Но на практике чаще используется возможность локального включения данного режима внутри командного файла специальной директивой:

После чего, можно обрабатывать принимаемые переменными значения внутри цикла, используя вместо знаков процента восклицательные знаки:

FOR … (
IF !ERRORLEVEL!==0 вместо %ERRORLEVEL%==0

)

&nbsp &nbsp В русскоязычной справке команды IF имеется ошибка, которая много лет переходит из версии в версию — вместо оператора EQU — равно , указано EQL — равно

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *