Разница между «|| exit / b» и «|| exit / b! Errorlevel!»

Exit /b in windows batch script

You must be familiar with the exit command. If you execute this command in a DOS window, the DOS window closes and the cmd process exits. This is like closing the window by clicking the “Close” button on the title bar of the cmd window.

exit /b is often seen in a Windows bat file. When the script runs to the “exit /b” line,  the commands after this line are not executed and the commands after the calling of current script continue. If current script was run/called directly by the cmd process, the cmd process won’t exit. This is exactly the aim of the /b(b means batch) switch. Without the /b switch, the script exits and the cmd running the script exits too. If you run exit /b in the cmd window(not in a bat script), the cmd exits.

You can provide an exit code to the exit command, i.e., exit [/b] exitCode. If the cmd process exits(as in the case you run the exit command in the cmd window or use “exit exitCode” in a bat script), the exitcode will be set to the exit code of the cmd process. If you use exit /b exitCode in a batch file, since the cmd does not end when the script ends, there would be no exit code to set, in that case, the ERRORLEVEL environment variable of the cmd process is set to the exitCode.

Loops

В главе, посвященной принятию решений, мы увидели утверждения, которые последовательно выполнялись одно за другим. Кроме того, реализации также могут быть выполнены в пакетном скрипте, чтобы изменить поток управления в логике программы. Затем они классифицируются в поток контрольных операторов.

В Batch Script нет прямого оператора while, но мы можем очень легко реализовать этот цикл, используя оператор if и метки.

Конструкция «FOR» предлагает возможности зацикливания для пакетных файлов. Ниже приведена общая конструкция оператора for для работы со списком значений.

Оператор for также может перемещаться по диапазону значений. Ниже приводится общая форма заявления.

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

Set и блок

Рассмотрим вызов скрипта через вызов cmd.exe с параметром /c (этот параметр приказывает выполнить строку, переданную в качестве аргумента, и завершить выполнение). Набор параметров cmd.exe и их назначение можно узнать из справки (источники указаны в начале статьи).

Вывод скрипта “4_1.bat”:

В отличие от встроенных команд, внешние приложения не в состоянии в момент своего завершения «сохранять» значения переменных среды (в этом, собственно, и заключается одно из отличий скриптов командной оболочки от скриптов наподобие Windows Scripting Host, требующих для своего выполнения создания нового «процесса»), которые они изменили или создали в процессе своей работы, (за исключением встроенных переменных, таких как ERRORLEVEL, которые изменяются самим командным процессором).

В этом отношении сам командный процессор cmd.exe является таким «внешним» приложением, т.к. переданный в качестве аргумента скрипт запускается в отдельном процессе cmd.exe, поэтому не удивительно, что значение переменной MYVALUE1 после выхода из «вложенного» cmd.exe не сохранилось.

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

Вывод скрипта “4_3.bat”:

Можно устранить этот «эффект» игнорирования кода возврата из скрипта внутри блока простым добавлением call перед названием скрипта.

Вывод скрипта “5_1.bat”:

Или просто не пользоваться “cmd.exe /c”.

Вывод скрипта “5_2.bat”:

К тому же, во втором случае после выхода из самого внешнего блока мы также добьёмся «сохранения» переменных, созданных (или изменённых) во «внешнем» скрипте или во внутреннем блоке.

Таким образом, мы решили две проблемы, а именно, сохранили значение переменной MYVALUE1 после выхода из блока и изменили значение ERRORLEVEL также после выхода из блока. Чтобы решить последнюю проблему с изменением переменных только после выхода из самого внешнего блока, достаточно будет удалить все блоки, а если блоки использовались совместно с оператором if, то заменить их соответствующими конструкциями goto метка (что, несомненно, ухудшит читаемость кода, но избавит от неявного поведения – прим.ред.).

Вывод скрипта “5_3.bat”:

Выполнение каких либо действий в заданный интервал времени.

Речь пойдет не о запуске командного файла в определенное время, а
о выполнении какой-то его части только в определенный интервал времени,
например, с 10:00 до 12:00. Решения данной задачи потребует сравнения
текущего времени с указанным промежутком.

Для примера, создадим командный
файл, выполняющий запуск стандартного калькулятора Windows , только в том
случае, если он выполняется в интервале времени от 10:00 до 12:00. Необходимо
получить текущее время и выполнить проверку того, что оно не больше 12:00 и
не меньше 10:00 .Команда IF позволяет выполнить сравнение строк
при использовании формата:

IF /I строка1 оператор_сравнения строка2 команда

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

EQU – равно

NEQ – не равно

LSS – меньше

LEQ – меньше или равно

GTR – больше

GEQ – больше или равно

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

REM Время меньше 12:00 – перейти на анализ второго условия, иначе – выход

if %time:~0,2% lss 12 goto tst2

exit

:tst2

Rem Время больше 10:00 – перейти к выполнению, меньше – завершить командный файл выполнив exit

if %time:~0,2% gtr 10 goto excalc

exit

:excalc

calc.exe

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

Выполнение команд по расписанию.

В операционных системах WINDOWS XP и старше существует утилита командной
строки AT.EXE, позволяющая управлять задачами для
планировщика заданий Windows, и таким образом, выполнить команду или
пакетный файл в указанное время на локальном или удаленном компьютере.

В операционных системах Windows 7 и старше, утилита at.exe присутствует, но признана устаревшей и нерекомендуемой к использованию в будущем.
Вместо нее рекомендуется использовать schtasks.exe, которая обладает большими возможностями, но сложнее в использовании.

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

Примеры команды AT [\имя_компьютера] [ [код] [/DELETE] | /DELETE [/YES]] AT [\имя_компьютера] время [/INTERACTIVE] [ /EVERY:день[,…] | /NEXT:день[,…]] “команда”

\имя_компьютера – имя удаленного компьютера. Если этот параметр
опущен, задача относится к локальному компьютеру. код – порядковый номер запланированной задачи.
Указывается если нужно отменить уже запланированную задачу с помощью
ключа /delete. /delete – отменить запланированную задачу.

Если код задачи опущен,
отменяются все задачи, запланированные для указанного
компьютера./yes – не будет запроса на подтверждение при отмене всех
запланированных задач.время – Время запуска команды./interactive – интерактивный режим, разрешение взаимодействия
задачи с пользователем.

Задачи, запущенные без этого ключа невидимы для
пользователя компьютера./every:день[,…] Запуск задачи осуществляется по указанным дням недели или
месяца. Если дата опущена, используется текущий деньмесяца./next:день[,…

Примеры использования:

Двойное раскрытие переменной

Данный пункт не относится к проблемным местам в написании скриптов, а скорее является так называемым хинтом.

Если в нашем распоряжении имеется переменная, которая уже имеет какое-то ранее присвоенное значение и имя этой переменной присутствует в значении другой переменной, чтобы раскрыть значение второй переменной так, чтобы значение первой переменной подставилось вместо ее “%”-имени в значение второй переменной, можно воспользоваться синтаксисом переустановки переменной через вызов call.

Вывод скрипта “9_1.bat”:

В этом случае всё, что записано справа от вызова call до первого неэкранированного спецсимвола раскрывается два раза. Первый раз – перед выполнением любой команды внутри скрипта, то есть перед интерпретацией строчки текста в скрипте (при этом раскрывается вся строчка целиком), второй раз – перед самим вызовом call (раскрывается часть строчки от call-команды до первого неэкранированного спецсимвола, например, перенаправления ввода-вывода). Для проверки достаточно создать скрипт с именем переменной и вызвать его рекурсивно изнутри.

Вывод скрипта “%9_2%.bat”:

ПРИМЕЧАНИЕ

Кстати, в скриптах в качестве имён переменных можно использовать числа и некоторые спецсимволы, а неинициализированные переменные, введённые непосредственно в командную строку консоли cmd.exe, не удаляются, как это происходит в скриптах, т.к. это могут быть обращения к именам файлов. Инициализированные переменные раскрываются всегда.

Поскольку в нашем примере переменная %9_2% не была инициализирована, то, в отличие от скрипта, где бы она была замещена «пустым местом» (или, попросту говоря, удалена), здесь она не была замещена. Что касается текста ошибки, то _2 – это всё, что осталось после раскрытия %9_2% внутри скрипта, где %9 – это встроенная неинициализированная переменная, а % – просто проигнорированный спецсимвол.

Устранить игнорирование двойного раскрытия несложно, достаточно заменить %~0 на %%~0.

Вывод скрипта “%9_3%.bat”:

Получили рекурсивный вызов, как и требовалось. Единственное, что можно было бы здесь рассмотреть ещё, – это раскрытие инициализированных переменных непосредственно в окне консоли.

Вывод скрипта “%9_3%.bat”:

Есть ли замена cmd.exe?

Есть. Существует множество замен, начиная от “неродных”, перенесенных из-под Unix и Linux, и заканчивая разработками, ориентированными только на Windows. Останавливаться подробно на «неродных» командных процессорах я бы здесь не хотел (самый известный из них – Cygwin, который не только предоставляет набор утилит, но и *NIX-совместимый API). Приведу список нескольких командных процессоров для Windows, которые я смог найти в интернете.

ПРИМЕЧАНИЕ

Некоторые командные процессоры, такие как 4NT, предлагают интеграцию на таком уровне, что скрипты, написанные под cmd.exe, свободно работают и под 4NT без каких-либо дополнительных модификаций, но это обманчиво. 4NT – это сторонний командный процессор, и он не в состоянии повторить все “багофичи” cmd.exe. Чтобы убедиться в этом, достаточно будет протестировать представленные здесь примеры.

На наш взгляд, в реальной работе имеет смысл использовать PowerShell, если вы полностью сосредоточены на работе с ОС Microsoft, или Cygwin, который существует на множестве платформ в неизменном виде и предоставляет знакомый unix-подобный интерфейс.

Запрет расширенного режима командного процессора

Для текущего сеанса пользователя:

Для всех пользователей:

Для запрета расширенного режима командного процессора — cmd.exe создайте в этом ключе параметр с именем «EnableExtensions» и значением «0». Для включения расширенного режима используйте значение «1». Перезапустите консоль, чтобы изменения вступили в силу.

:/>  6 Причин Тихого Звука в Windows

Расширенный режим командного процессора можно также включить или отключить для консольного процесса с помощью параметра /e:{on|off} или несколько короче: /x – включение, /y – выключение.

Расширенный режим вносит изменения и дополнения в следующие команды:

  • DEL или ERASE
  • COLOR
  • CD или CHDIR
  • MD или MKDIR
  • PROMPT
  • PUSHD
  • POPD
  • SET
  • SETLOCAL
  • ENDLOCAL
  • IF
  • FOR
  • CALL
  • SHIFT
  • GOTO
  • START (также вносит изменения в семантику вызова скриптов и утилит вне команды START)
  • ASSOC
  • FTYPE

Изменение даты и времени файлов или папок.

В Windows, к сожалению, не существует стандартного консольного средства для изменения даты и времени создания, доступа и модификации файлов и папок. Кто имел дело с Unix/Linux знает, что в этих операционных системах имеется простая и удобная утилита
touch с помощью которой можно изменить время последнего доступа или время модификации файла на текущее значение или на значение времени,
заданное в качестве аргумента командной строки.

Не секрет, что командные оболочки Unix/Linux значительно превосходит по своим возможностям командную строку Windows, поэтому, вполне понятно появление пакета UNIX Shell and Utilities для Windows NT и старше. Этот пакет представляет собой Windows-версии наиболее популярных утилит из среды Unix/Linux , которые могут выполняться как консольные команды в среде оболочки пользователя (в среде модуля sh.exe, входящего в пакет) или как исполняемые файлы в командной строке Windows.

Команда call и блок

Внутри справки (внешней справки – ntcmds.chm, см. начало статьи) по командному процессору на счёт команды call можно прочитать интересное заявление, что якобы она не останавливает выполнение скрипта родителя, т.е. продолжает выполнение запускаемого скрипта параллельно (выделено курсивом):

Call – Calls one batch program from another withoutstopping the parent batch program.

В общем случае это не так, и не имеет ничего общего с действительным поведением по умолчанию. Скрипт-родитель ждёт завершения вызванного скрипта.

ПРИМЕЧАНИЕ

Следует сделать уточнение, если вызывается не скрипт, а внешняя утилита, то сценарий выполнения зависит от этой утилиты. Если она была собрана (создана), как консольная, то скрипт ждёт завершения её выполнения. Если как GUI, то не ждёт.

Но суть не в этом, вообще команда call обладает некоторым «магическим» действием. То, что раньше не работало, с её использованием начинает работать.

Вывод скрипта “8_1.bat”

Добавим call.

Вывод скрипта “8_2.bat”:

Вообще, такое поведение свойственно блоку, поэтому в данном случае использование call-вызова лишь устраняет «дефекты работы» блока.

Команда if и переменные %0-%9

Порой можно встретить интересные записи условий с использованием переменных-аргументов. Вот некоторые из них.

Или пример из скрипта vcvarsall.bat в Microsoft Visual Studio 2005:

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

Вывод скрипта “15_3.bat”:

Сообщение об ошибке относится к третьей строчке с if. Попробуем разобраться, что произошло, а точнее, просто подставить “My Value” (вместе с кавычками под %1).

Вывод скрипта “15_4.bat”:

Как видно, ничего не изменилось, но стало понятно, какую строчку препроцессор не пропускает. Этого можно избежать, если всегда внутри условий использовать кавычки и “~”-модификации встроенных переменных %0-%9.

Вывод скрипта “15_5.bat”:

Исключение составляет сравнение чисел вместо строк. В этом случае использование кавычек может привести к неверному результату.

Вывод скрипта “15_6.bat”:

Вывод скрипта “15_7.bat”:

Определение доступности ip-адреса

Для проверки доступности сетевого узла чаще всего используется упоминаемая выше стандартная утилита ping.exe. Утилита выполняет отправку ICMP-пакета на проверяемый узел (эхо-запрос) и ожидает ответный пакет (эхо-ответ). К сожалению, результат проверки доступности узла не в полной мере коррелируется со значением переменной ERRORLEVEL и может быть получен только из данных стандартного вывода ping.

В разных версия ОС Windows, утилита ping.exe может устанавливать нулевое значение ERRORLEVEL даже при недоступности узла, но при наличии в командной строке верных параметров командной строки. Иными словами, в некоторых случаях, требуемый сценарием результат выполнения определенной команды трудно, или вообще нельзя определить по значению переменной ERRORLEVEL, и приходится анализировать более достоверные признаки успешного или неуспешного выполнения команды, например, наличие определенного текста в ее выводимых сообщениях.

Если внимательно посмотреть на сообщения программы ping.exe при опросе доступного и недоступного узла, то можно заметить, что они значительно отличаются :ping 456.0.0.1 – ping на несуществующий адрес

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

При проверке связи не удалось обнаружить узел 456.0.0.1. Проверьте имя узла и повторите попытку.

Определение текущей версии windows.

Так же, как и в предыдущем примере, для определения версии операционной системы в процессе выполнения командного файла, можно воспользоваться поиском определенных фрагментов текста в результатах выполнения команд, отображающих сведения о системе. Например, во всех операционных системах семейства Windows ( и даже в DOS ) существует специальная команда VER, предназначенная для отображения сведений о версии ОС. В результате выполнения команды, например, в среде Windows XP, отображается текст:

Microsoft Windows XP [Версия 5.1.2600]

В среде Windows 7, текст отличается:

Microsoft Windows [Version 6.1.7600]

В среде Windows 10:

Microsoft Windows [Version 10.0.14393]

Таким образом, результат выполнения команды VER в среде разных версий Windows,
всегда содержит определенный текст, характерный только для данной ОС, и задача определения версии решается довольно просто:

Переходы и метки.

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

Условия таковы – есть 2 съемных диска, один из которых должен быть виден в проводнике как диск X: а второй – как диск Y: независимо от того, в какой
порт USB они подключены и какие буквы присвоены им операционной системой.
Для примера, будем считать, что реальные диски могут быть подключены как F: или G:

Flashd1.let – на первом диске

Flashd2.let – на втором

Таким образом, задача командного файла заключается в том, чтобы проверить
наличие на сменных дисках F: и G: файлов Flashd1.let
или Flashd2.let и, в зависимости от того, какой из них присутствует,
присвоить диску букву X: или Y:

Для поиска файла на диске воспользуемся командой IF EXIST:

IF EXIST имя_файла команда

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

SUBST X: C: – – создать виртуальный диск
X:, содержимым которого будет корневой каталог диска C:

   
Для решения поставленной задачи, создаем командный файл, например с именем setletter.bat, следующего содержания:

Поиск в локальной сети включенных компьютеров.

В данном примере речь идет о создании командного файла, позволяющего “собрать” список IP-адресов узлов локальной сети, включенных на данный момент времени. Ничего принципиально нового в плане создания командных файлов здесь нет, но тем не менее, задача поиска включенных узлов в локальной сети встречается довольно часто, и решить ее описанными выше способами, с использованием ping.exe и net view удается далеко не всегда, поскольку в современных версиях операционных систем семейства Windows настройки брандмауэров по умолчанию, задают довольно жесткие правила, блокирующие сетевые соединения извне, и ответы на эхо-запрос.

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

При любой передаче пакетов IP-протокола с данного компьютера на IP-адрес другого сетевого устройства в локальной сети, программные средства сетевых протоколов выполняют процедуру определения физического адреса сетевого адаптера получателя ( MAC-адрес получателя).

Всем сетевым устройствам отправляется специальный широковещательный запрос ( запрос который будет принят всеми компьютерами данной подсети), означающий “чей MAC – адрес соответствует такому-то IP-адресу”. Если какое-либо сетевое устройство опознало свой собственный IP-адрес, оно отправит ARP-ответ, содержащий соответствующий MAC-адрес, который будет сохранен в специальной таблице соответствия адресов IP и MAC, хранящейся в оперативной памяти компьютера, отправившего ARP-запрос.

Запись информации в данную таблицу выполняется только при необходимости передачи любых данных по протоколу IP, что можно инициировать, например, пингованием опрашиваемого устройства. Даже если настройками брандмауэра полностью закрыты все соединения извне и блокируется протокол ICMP ( устройство не «пингуется» ) , в буферной памяти сервиса
ARP будет присутствовать запись соответствия IP и MAC, если устройство было подключено к локальной сети и участвовало в процедуре разрешения адреса .

Для просмотра содержимого ARP-кэш можно воспользоваться командой

arp -a – отобразить все записи в таблице ARP

Пример отображения таблицы ARP:

Интерфейс: 192.168.0.29 — 0xa

адрес в Интернете Физический адрес Тип

192.168.0.1 00-1e-13-d6-80-00 динамический

192.168.0.3 60-eb-69-08-18-d2 динамический

. . .

Интерфейс: 192.168.234.1 — 0xf

адрес в Интернете Физический адрес Тип

192.168.234.255 ff-ff-ff-ff-ff-ff статический

224.0.0.22 01-00-5e-00-00-16 статический

224.0.0.252 01-00-5e-00-00-fc статический

239.255.255.250 01-00-5e-7f-ff-fa статический

Как видно из приведенной таблицы, например IP – адресу 192.168.0.1 соответствует физический адрес сетевого адаптера, равный 00-1e-13-d6-80-00 . Если же сетевой адаптер с данным адресом будет недоступен, то такой записи в таблице не будет.

Для понимания алгоритма опроса сети необходимо учесть следующее:

Разрешение адресов ARP используется только при передаче данных по IP-протоколу в пределах сегмента локальной сети, задаваемого маской. Так, например, для примера с IP адресом 192.168.0.1 и маски 255.255.255.0 это будет диапазон IP от 192.168.0.1 до 192.168.0.254.

Поиск компьютеров с запущенным приложением по списку

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

Список можно получить из сетевого окружения с использованием команды:
net.exe view > comps.txt
После выполнения такой команды файл
comps.txt
будет содержать список
следующего вида:

Имя сервера                 Заметки< 2 пустых строки >——-\AB1\AB2\ALEX\BUHCOMP\PC2\SA\SERVERКоманда выполнена успешно.


   
Обрабатывать содержимое этого текстового файла будем с помощью команды
FOR
с ключом /F:

FOR /F [“ключи”] %переменная IN (имя файла) DO команда [параметры]

Данная команда позволяет получить доступ к строкам в текстовом файле с использованием ключей:skip=n                 – пропустить n строк от начала файла (в нашем примере – 4 строки)eol=< символ >  – не использовать строки, начинающиеся с заданного символа.
(в нашем случае – пропустить последнюю строку, начинающуюся с кириллического символа “К” – “Команда выполнена успешно”tokens=n             – брать для обработки n-е слово в строке (в нашем случае – 1-е слово)

Окончательный вид команды:

Предисловие

Желание организовать собранный материал в статью появилось после некоторых исследований работы bat-скриптов и накопления опыта по их составлению в проекте под Microsoft Visual Sudio 2005, интенсивно использующего промежуточные bat-скрипты для взаимодействия непосредственно с некоторыми сторонними консольными утилитами, участвующими в сборке.

:/>  Изменение каталога запуска по умолчанию для командной строки в Windows 7

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

Статья предусматривает, что читатель уже знаком с написанием bat-скриптов NT (далее просто скриптов), знает о применении команд и умеет пользоваться справкой по встроенным командам и прилагаемым утилитам командного процессора. В статье не рассматриваются сторонние утилиты, а также их проблемы и особенности работы.

ПРИМЕЧАНИЕ

Некоторые проблемы, найденные в скриптах, могут быть связаны с их использованием (запуском) из проекта для Microsoft Visual Studio 2005 (C ), а также, возможно, и других сред, использующих обращения к bat-скриптам.

В конце статьи приведены тексты bat-скриптов и ссылки на некоторые консольные утилиты, использовавшиеся при тестировании примеров.

Присвоение съемному диску одной и той же буквы.

Задача заключается в том, чтобы съемный USB диск (флэш диск) был доступен
всегда под одной и той же буквой, независимо от того, на каком компьютере он
используется и каким образом он был подключен. Для ее решения воспользуемся уже упоминаемой выше командой SUBST, но реализуем присвоение новой буквы диску с
помощью подстановочного значения переменной %0, создаваемой
системой при каждом запуске командного файла.

Выберем для съемного диска желаемую букву, например – X.

Некоторые из переменных окружения, в том числе и переменная %0,
принимающая значение пути и имени выполняющегося командного файла, позволяют
при определенной модификации с использованием специального признака –
символа ” ~ “, получить ее частичное значение (расширение переменной).

Работа с графическими приложениями windows.

Допустим, вам нужно из одного и того же командного файла запустить notepad.exe
и cmd.exe. Если просто вставить строкиnotepad.execmd.exeто после запуска notepad.exe выполнение командного файла приостановится и пока не
будет завершен notepad, cmd.exe не запустится.

start /MAX notepad.exestart “This is CMD.EXE” /MIN cmd.exenet send %COMPUTERNAME% NOTEPAD and CMD running.

После выполнения этого командного файла вы увидите стартовавшие, в развернутом окне
(ключ /MAX) блокнот, в свернутом окне (ключ /MIN) командный процессор CMD.EXE и
окно с сообщением net.exe. Стандартный заголовок окна cmd.exe заменен на текст
“This is CMD.EXE”.

Обратите внимание на то что заголовок окна можно опускать, но
особенность обработки входных параметров командой start может привести к
неожиданным результатам при попытке запуска программы, имя или путь которой содержит
пробел(ы). Например при попытке выполнить следующую команду:start “C:

Program FilesFARFAR.EXE”Из-за наличия пробела в пути к исполняемому файлу,
строка для запуска FAR.EXE должна быть заключена в двойные
кавычки, однако формат входных параметров для start предполагает наличие заголовка
окна, также заключаемого в двойные кавычки, в результате чего “C:

   

Если вам все же потребуется расширенное управление окнами приложений,
придется воспользоваться сторонним программным обеспечением, например, широко известная утилита cmdow.exe

Версия cmdow.zip, используемая при написании этой статьи. ZIP-архив, закрытый паролем novirus

Разница между «|| exit / b» и «|| exit / b! errorlevel!»

У нас есть несколько .batскриптов сборки, которые запускаются бегуном GitLab на основе PowerShell, который недавно был переработан с:

program args
if !errorlevel! neq 0 exit /b !errorlevel!

к более сжатым:

program args || exit /b

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

program args || exit /b

но, похоже, это сработало, когда первое – нет:

program args || exit /b !errorlevel!

Я прочитал вариант b пакетного выхода Windows на вопрос SO с уровнем ошибок или без него и приведенное ниже заявление с https://www.robvanderwoude.com/exit.php, но все еще не могу полностью объяснить, что я наблюдаю.

The DOS online help (HELP EXIT) doesn’t make it clear that the /B parameter exits the current instance of script which is not necessarily the same as exiting the current script.
I.e. if the script is in a CALLed piece of code, the EXIT /B exits the CALL, not the script.


Это минимальный пакетный файл, который я использовал для изучения этого:

@echo off
setlocal EnableDelayedExpansion
cmd /c "exit 99" || exit /b
:: cmd /c "exit 99" || exit /b !errorlevel!

И вот как я вызвал командный файл (чтобы имитировать, как он был вызван бегуном на основе GitLab PowerShell):

& .test.bat; $LastExitCode

Вот результат, зависящий от того, какая из двух строк в командном файле выполняется:

PS> & .test.bat; $LastExitCode
0
PS> & .test.bat; $LastExitCode
99

Есть еще один способ добиться правильного поведения, который состоит в том, чтобы вызвать пакетный файл от руки из PowerShell, используя CALLтакже:

PS> & cmd.exe /c "call .test.bat"; $LastExitCode
99

Хотя я понимаю, что это может быть правильный способ вызова командного файла из PowerShell, это не кажется общеизвестным на основании многих примеров, которые я видел. Также мне интересно, почему PowerShell не вызывает пакетный файл таким образом, если это «правильный путь». И, наконец , я до сих пор не понимаю , почему, когда убрав callповедение меняется в зависимости от того , мы добавим !errorlevel!к exit /bзаявлению.


ОБНОВЛЕНИЕ: Спасибо за все обсуждения до сих пор, но я чувствую, что он теряется в сорняках, что, вероятно, является моей ошибкой в ​​том, что я слишком расплывчатый в моем первоначальном вопросе. Я думаю, что мне действительно нужно (если возможно) окончательное утверждение о том, когда errorlevel(предположительно) оценивается в следующем утверждении:

program || exit /b

Действительно ли он оценивается раньше (то есть до programзапуска) следующим образом:

program || exit /b %errorlevel%

Или он оценивается лениво (т.е. когда exitвыполняется после programвыполнения и внутренне errorlevelобновлен), что более аналогично этому:

program || exit /b !errorlevel!

Следовательно, я на самом деле не гонюсь за спекуляциями, если, к сожалению, это лучшее, что мы можем сделать, и в этом случае, зная, что нет окончательного ответа или что это ошибка, является приемлемым ответом для меня: o).

Сброс errorlevel

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

Вывод скрипта “1_4.bat”:

Чтобы правильно сбросить код возврата, можно воспользоваться внешней утилитой, которая вернёт заведомо нулевой код возврата, либо встроенной командой, которая предусматривает изменение кода возврата (к примеру, cd).

Вывод скрипта “1_5.bat”:

ПРИМЕЧАНИЕ

ERRORLEVEL в 32-битной операционной системе Windows NT является 32-битным целым числом со знаком, т.е. представлена диапазоном значений: от -2147483648 до 2147483647. Таким образом, приложения могут возвращать отрицательный код возврата. Другие приложения могут не считать отрицательные значения за ошибку, к таким приложениям относится, к примеру, Microsoft Visual Studio 2005. Поэтому следует проверять код возврата на знак, если использующее скрипт внешнее приложение или скрипт также проверяет код возврата на знак.

Можно также воспользоваться скриптом, который сможет установить переданный ему код возврата (как все скрипты, внутри других скриптов его следует вызывать с применением оператора call).

Результат работы скрипта “errlvl.bat”:

Спецсимволы в переменных

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

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

Вывод скрипта “17_1.bat”:

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

Вывод скрипта “17_2.bat”:

Но дело может не ограничиваться обычным выводом.

Вывод скрипта “17_3.bat”:

Можно обернуть всё выражение команды set в кавычки.

Вывод скрипта “17_4.bat”:

Или поступить несколько иначе:

Вывод скрипта “17_5.bat”:

Проблемой в данном случае при двойном раскрытии переменной через оператор call будет «авто-экранирование» (дублирование) спецсимвола

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

Тестирование

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

ПРЕДУПРЕЖДЕНИЕ

Здесь и далее все bat-скрипты тестировались в основном только под Windows XP Service Pack 2 (Build 2600.xpsp_sp2_gdr.070227-2254 : Service Pack 2). Возможно, это справедливо и для системы без сервис паков или с предыдущими сервис паками, но это особым образом не проверялось.

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

Всё сказанное в статье справедливо для так называемого расширенного режима cmd.exe (установлена переменная CMDEXTVERSION).

Удаление переменной

Операция удаления переменной может вернуть ненулевой код возврата, если удаляется переменная, которая не существует. Чтобы каждый раз не писать проверку на существование переменной, можно воспользоваться скриптом unset.bat, который проверяет существование переменной перед её удалением. Скрипт приведён в приложении.

То же самое может быть справедливо и для установки переменной. К примеру, оператор for использовует встроенные аргументы %i, %j и т.д (см. справку) для управления циклом. Их значения в общем случае могут отсутствовать.

Поэтому, если попытаться для установки значения некоторой переменной скрипта использовать один из этих аргументов, можно получить ненулевой код возврата, так как присвоение отсутствующей переменной пустого (не заданного) значения будет интерпретировано cmd.exe как попытка удалить значение несуществующей переменной. В примере 1.3.1 демонстрируется эта ситуация.

Происходит это из-за того, что значений аргументов %j и др. может не существовать в момент присвоения внутри for-блока. Вместо того, чтобы проверять их на существование, можно в данном случае – всегда устанавливать значение переменной, к примеру, в 0 перед присвоением ей значения for-аргумента.

Урок 10 по cmd – пользовательские подпрограммы, процедуры и функции |

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

функции командной строки

И так, давайте посмотрим, что именно подразумевается под этими терминами:

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

Давайте рассмотрим такой пример:

Видим, что для объявления метки, перед ее именем ставится двоеточие, тут мы объявили три метки: SUB1, SUB2, SUB3 и EXIT. В самом начале сценариях мы отключаем вывод эхо-сообщений с помощью функции командной строки echo. Далее идет проверка условия с помощью условного оператора if, в нем мы проверяем первый переданный аргумент сценарию (%1):

:/>  Установщик Windows

Если аргумент пустой, то происходит вывод сообщения, что надо передать аргумент сценарию, далее сразу происходит переход к метке EXIT. Видим, что тут мы сгруппировали две команды с помощью оператора &.

Если аргумент равен 1, то идет переход к метке SUB1. Тут мы создаем переменную var1 с помощью ключевого функции set командной строки, в переменной к первому аргументу прибавляется число 100, потом следует вывод результата, удаление переменной и переход к метке EXIT.

Если аргумент равен 10, то идет переход к метке SUB2. Аналогично, создается переменная var2 и происходит прибавление к аргументу 1.

Если аргумент равен 100, то идет переход к метке SUB3. Тут уже прибавляется 1000.

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

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

Тут происходит создание переменной sum со значением 1. После метки SUB1 происходит добавление к sum числа 1, вывод результата и переход к этой же метке. Цикл будет выполняться бесконечно, что бы прекратить выполнение сценария нажмите комбинацию клавиш Ctrl C.

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

Давайте посмотрим такой пример:

В данном примере созданная переменная var будет хранить путь к файлу test.bat (в моем случае это текущий сценарий). Потом мы вызываем программу notepad.exe (блокнот) , передавая ей путь к файлу. Как только мы закроем блокнот, произойдет вызов калькулятора. После завершения работы программы calc.exe (калькулятор) будет вызвана функция командной строки dir и произойдет вывод содержимого текущего каталога. В самом конце мы удаляем переменную var.

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

Вы можете написать и такой сценарий:

это аналогично такому коду:

или такому:

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

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

Далее следует вызов метки FACT со значение переданного параметра. После метки FACT происходит проверка на равенство 0 и 1, оператор exit/b говорит, что нужно произвести возврат в т место, откуда была вызвана данная строка. То есть, произойдет возврат в начало сценария к строке, следующей за строкой call :FACT %1, то есть, произойдет вывод содержимого переменной Result с помощью функции командной строки echo и переход к метке EXIT.

Если переданный параметр не равен 0 или один, то от него отнимается единиц и происходит снова возврат к метке FACT. Фактически мы циклично уменьшаем переданное значение до 1 и потом снова циклично умножаем его на значение, меньшее на 1.

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

Спасибо за внимание. Автор блога

Условный блок и errorlevel

С предыдущей проблемой связана проблема задания значения встроенной переменной ERRORLEVEL внутри условия-блока. Фактически задание значения переменной ERRORLEVEL происходит за счёт выполнения командным процессором некоторой подпрограммы, в данном случае это будет ни что иное, как псевдокоманда “set ERRORLEVEL=<код возврата>” сразу же после выхода из вызванного приложения или скрипта.

Это понятно по признакам работы обычной команды set. Таким образом, все проблемы, связанные с командой set, «автоматически наследуются» и подпрограммой командного процессора, устанавливающей значение переменной ERRORLEVEL.

Для проверки используется консольная утилита errlvl.exe, назначение которой – возвращать код ошибки, переданный ей в качестве аргумента. Эта утилита делает примерно то же самое, что и скрипт errlvl.bat (если вы захотите потом заменить её на этот скрипт, не забудте добавить к его вызову оператор call, иначе результат будет отличаться от ожидаемого!).

Вывод скрипта “3_1.bat”:

Вывод скрипта “3_2.bat”:

Часто встречающиеся ошибки при написании командных файлов.

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

Обычно, это вызвано тем, что вы не учитываете тот факт, что на момент выполнения вашего командного файла переменные среды могут быть совсем другими, чем на момент его написания и запуска из командной строки. Например, в командном файле используется запуск приложения myprog.exe, находящегося в каталоге SCRIPTS на диске D: . Если в командном файле используется имя исполняемого модуля без полного пути

MYPROG.EXE и если каталог D:SCRIPTS не прописан в путях поиска (переменная PATH ) то модуль MYPROG.EXE может быть найден и выполнен только если текущим каталогом является D:SCRIPTS. Но если вы укажете полный путь к myprog.exe:D:SCRIPTSmyprog.exeто программа будет найдена и выполнена в любом случае.

Кроме того, нередко программа, указанная в командном файле использует для поиска своих компонент (dll, ini и т.п. ) собственный каталог. Но на момент ее выполнения текущим каталогом может быть любой (чаще всего – системный каталог Windows). Естественно, компоненты не находятся и программа не выполняется.

Rem Сменим текущий дискD:Rem перейдем в каталог SCRIPTSCD D:SCRIPTSmyprog.exe

Также для переходов по каталогам можно воспользоваться командами pushd и popd, описание и примеры использования которых имеются в разделе сайта со списком команд Windows.

Неправильно отображаются русские имена файлов, служб и т.п.

Причина в том, что при создании командных файлов вы
использовали текстовый редактор, в котором русские символы представлены
не в DOS-кодировке. Если в приведенном выше примере перезапуска службы “DNS-клиент”
вы используете неверную кодировку, то русская часть имени службы не будет опознана
из-за неверной кодировки и будет выдано сообщение, что указанная служба не
установлена.

Чтобы избежать проблем с русскими символами в командных файлах,
используйте редактор с поддержкой DOS-кодировки, например, встроенный редактор
файлового менеджера Far Manager. Переключение между кодировками в редакторе осуществляется нажатием F8 .

Командный файл выполняется на одном компьютере успешно, но на другом – не работает.

Обычно это вызвано применением в командных файлах абсолютных значений для дисков, файлов и каталогов вместо переменных среды окружения. Вместо C:WINDOWS правильнее использовать %SYSTEMROOT%, потому,
что на другом компьютере система может быть установлена в другой каталог или на другой диск.

    Строки с переменными, принимающими значения имен файлов и каталогов лучше
заключать в кавычки. Командная строка

DIR %ProgramFiles%

не выдаст вам содержимого каталога C:Program Files , поскольку из-за наличия пробела будет
интерпретирована как

DIR C:Program

Командная строкаDIR “%ProgramFiles%”выполнится верно.

    Старайтесь использовать команды Setlocal и Endlocal, чтобы не оставлять мусор
из переменных, созданных или модифицированных командным файлом.

Использование командных файлов в сценариях регистрации
пользователей .

   
Командные файлы
удобно использовать для выполнения каких-либо
действий при регистрации пользователя в
домене. Делается это с помощью вкладки
Profile

свойств пользователя домена.

Сами командные файлы должны
находиться в сетевой папке
Netlogon
(WINDOWSSYSVOL DOMAINSCRIPTS) контроллера домена.

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

Дополнительные материалы по командной строке Windows:

Раздел со списком команд
CMD Windows. Имена большинства команд представляют собой ссылку на
страницы с их описанием и примерами.

Настройка окна командной строки
Windows. Малоизвестные возможности по изменению свойств консоли Windows.

Работа с сетью в
командной строке Windows – Описание и примеры использования
утилит командной строки для работы с сетью.

Запуск командных файлов от имени администратора без запроса UAC – использование планировщика заданий для запуска приложений
от имени Администратора без запроса системы контроля учетных записей UAC.

Основные приемы, используемые при работе в командной строке Windows.

Если вы желаете поделиться ссылкой на эту страницу в своей социальной сети, пользуйтесь кнопкой “Поделиться”

В начало страницы         |        
На главную страницу сайта

Заключение

Я постарался привести здесь всё, с чем мне приходилось столкнуться при написании bat-скриптов, но не исключено, что что-то могло быть упущено.

Итак, мы пришли к некоторому набору условий и правил, которые следует выполнять и помнить для написания корректных скриптов, а это:

  1. Не забывать там, где это необходимо, экранировать приоритетные спецсимволы;
  2. Не вставлять пробелы при присваивании переменных командой set и не использовать в ней кавычки в значениях переменных в качестве ограничивающих символов;
  3. При возможном использовании спецсимволов в значениях переменных, пользоваться кавычками вокруг выражения Variable=Value выражения set Variable=Value.
  4. При установке переменной убедиться, что правая часть выражения не может быть пустой.
  5. Сбрасывать переменные с помощью вспомогательного скрипта unset.bat.
  6. По возможности вместо условия-блока использовать условие-переход.
  7. При использовании блока (оператор “скобки”) помнить, что фактическое присваивание переменных (включая установку кода возврата) выполняется после выхода из самого внешнего блока, располагающегося внутри выполняемого скрипта;
  8. Всегда вызывать скрипты из других скриптов через оператор call.
  9. Всегда завершать выполнение скрипта с текущим кодом возврата через goto :EOF вместо exit /b %ERRORLEVEL%.
  10. При вызове call внутри скрипта помнить, что строка call (до первого спецсимвола перенаправления ввода/вывода) раскрывается 2 раза, т.е. все “%”-переменные в ней раскрываются 2 раза, а символ « – автоэкранируется;
  11. Всегда пользоваться кавычками в выражениях условий, если сравниваются строки и не использовать, если числа;
  12. Помнить, что вызов скриптов с аргументами приводит к передаче кавычек внутрь переменных %0-%9, поэтому там, где надо (к примеру, в выражениях условий), использовать вместо них переменные %~0-%~9;
  13. Использовать команды setlocal/endlocal в случае необходимости удалять и восстанавливать промежуточные переменные, соответственно, создаваемые и изменяемые в процессе работы какого-либо скрипта.

Оставьте комментарий

Adblock
detector