command line – How to call CMD without opening a window – Stack Overflow

Call-вызов скрипта и переменные %0-%9

При вызове внешней утилиты с параметрами, например, printargs.exe, аргументы передаются в неё без ограничивающих кавычек. Другое дело – вызов скрипта с аргументами, здесь всё иначе. При заключении аргументов в кавычки, кавычки передаются вместе со значениями и так сохраняются во встроенные переменные %0-%9.

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

ПРИМЕЧАНИЕ

Символы « » добавлены для более ясной картины происходящего.

Для устранения кавычек, если они имеются, нужно пользоваться “~”-модификацией этих переменных (полное описание – help call в окне консоли).

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

ПРИМЕЧАНИЕ

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

Errlvl.exe/errlvl.bat

Устанавливает значение переменной ERRORLEVEL, переданное утилите/скрипту в качестве аргумента. С их помощью можно также сбросить значение, передав им ноль в качестве параметра. Сбросить код возврата можно также, вызвав команду, устанавливающую код возврата, например «cd .».

Exit /b %errorlevel% и блок

Можно сначала подумать, что следующий пример, вопреки использованию call, всё равно не сохраняет код возврата.

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

Но это происходит из-за того, что ERRORLEVEL на протяжении всего выполнения равен 0, и принимает новое значение только после выхода из самого внешнего блока. Причем это значение не равно 10, так как операция exit /b %ERRORLEVEL% сбрасывает его в 0. Достаточно заменить эту операцию эквивалентной, но приводящей к правильному результату – goto :EOF.

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

Expandvar.bat

Раскрывает строку, переданную вторым аргументом, и сохраняет значение в переменную, имя которой передано первым аргументом. Скрипт восстанавливает код возврата, сбрасываемый оператором call внутри скрипта.

How to call cmd without opening a window

You can use the /C /Q switch

cmd.exe /c /q dir/b

this will run the dir/b command and exit no window will be shown since we are settng the ECHO off with the /q, but if you want to see the output before it closes then don’t use the /q switch as

cmd.exe /c dir/b

The above two examples will be too quick to execute so please try with this

cmd /c  dir/b/s

cmd /c /q dir/b/s

Since the dir /b /s will run through each sub-directory you can see it working.

In case you want to run the command with no window still want to get the output then pipe the clip command with it.

cmd /c dir/b|clip

This will copy the output of the command in the clipboard and that you can paste ti elsewhere, if the clipboard is not overwritten.

This has been tested in Windows 7 with Microsoft Windows [Version 6.1.7601] it might differ with other systems

For more information on cmd.exe just type cmd /? in the prompt to get the following:

Starts a new instance of the Windows command interpreter

CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
    [[/S] [/C | /K] string]

/C      Carries out the command specified by string and then terminates
/K      Carries out the command specified by string but remains
/S      Modifies the treatment of string after /C or /K (see below)
/Q      Turns echo off
/D      Disable execution of AutoRun commands from registry (see below)
/A      Causes the output of internal commands to a pipe or file to be ANSI
/U      Causes the output of internal commands to a pipe or file to be
        Unicode
/T:fg   Sets the foreground/background colors (see COLOR /? for more info)
/E:ON   Enable command extensions (see below)
/E:OFF  Disable command extensions (see below)
/F:ON   Enable file and directory name completion characters (see below)
/F:OFF  Disable file and directory name completion characters (see below)
/V:ON   Enable delayed environment variable expansion using ! as the
        delimiter. For example, /V:ON would allow !var! to expand the
        variable var at execution time.  The var syntax expands variables
        at input time, which is quite a different thing when inside of a FOR
        loop.
/V:OFF  Disable delayed environment expansion.

Note that multiple commands separated by the command separator '&&'
are accepted for string if surrounded by quotes.  Also, for compatibility
reasons, /X is the same as /E:ON, /Y is the same as /E:OFF and /R is the
same as /C.  Any other switches are ignored.

If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:

    1.  If all of the following conditions are met, then quote characters
        on the command line are preserved:

        - no /S switch
        - exactly two quote characters
        - no special characters between the two quote characters,
          where special is one of: &<>()@^|
        - there are one or more whitespace characters between the
          two quote characters
        - the string between the two quote characters is the name
          of an executable file.

    2.  Otherwise, old behavior is to see if the first character is
        a quote character and if so, strip the leading character and
        remove the last quote character on the command line, preserving
        any text after the last quote character.

If /D was NOT specified on the command line, then when CMD.EXE starts, it
looks for the following REG_SZ/REG_EXPAND_SZ registry variables, and if
either or both are present, they are executed first.

    HKEY_LOCAL_MACHINESoftwareMicrosoftCommand ProcessorAutoRun

        and/or

    HKEY_CURRENT_USERSoftwareMicrosoftCommand ProcessorAutoRun

Command Extensions are enabled by default.  You may also disable
extensions for a particular invocation by using the /E:OFF switch.  You
can enable or disable extensions for all invocations of CMD.EXE on a
machine and/or user logon session by setting either or both of the
following REG_DWORD values in the registry using REGEDIT.EXE:

    HKEY_LOCAL_MACHINESoftwareMicrosoftCommand ProcessorEnableExtensions

        and/or

    HKEY_CURRENT_USERSoftwareMicrosoftCommand ProcessorEnableExtensions

to either 0x1 or 0x0.  The user specific setting takes precedence over
the machine setting.  The command line switches take precedence over the
registry settings.

In a batch file, the SETLOCAL ENABLEEXTENSIONS or DISABLEEXTENSIONS arguments
takes precedence over the /E:ON or /E:OFF switch. See SETLOCAL /? for details.

The command extensions involve changes and/or additions to the following
commands:

    DEL or ERASE
    COLOR
    CD or CHDIR
    MD or MKDIR
    PROMPT
    PUSHD
    POPD
    SET
    SETLOCAL
    ENDLOCAL
    IF
    FOR
    CALL
    SHIFT
    GOTO
    START (also includes changes to external command invocation)
    ASSOC
    FTYPE

To get specific details, type commandname /? to view the specifics.

Delayed environment variable expansion is NOT enabled by default.  You
can enable or disable delayed environment variable expansion for a
particular invocation of CMD.EXE with the /V:ON or /V:OFF switch.  You
can enable or disable delayed expansion for all invocations of CMD.EXE on a
machine and/or user logon session by setting either or both of the
following REG_DWORD values in the registry using REGEDIT.EXE:

    HKEY_LOCAL_MACHINESoftwareMicrosoftCommand ProcessorDelayedExpansion

        and/or

    HKEY_CURRENT_USERSoftwareMicrosoftCommand ProcessorDelayedExpansion

to either 0x1 or 0x0.  The user specific setting takes precedence over
the machine setting.  The command line switches take precedence over the
registry settings.

In a batch file the SETLOCAL ENABLEDELAYEDEXPANSION or DISABLEDELAYEDEXPANSION
arguments takes precedence over the /V:ON or /V:OFF switch. See SETLOCAL /?
for details.

If delayed environment variable expansion is enabled, then the exclamation
character can be used to substitute the value of an environment variable
at execution time.

You can enable or disable file name completion for a particular
invocation of CMD.EXE with the /F:ON or /F:OFF switch.  You can enable
or disable completion for all invocations of CMD.EXE on a machine and/or
user logon session by setting either or both of the following REG_DWORD
values in the registry using REGEDIT.EXE:

    HKEY_LOCAL_MACHINESoftwareMicrosoftCommand ProcessorCompletionChar
    HKEY_LOCAL_MACHINESoftwareMicrosoftCommand ProcessorPathCompletionChar

        and/or

    HKEY_CURRENT_USERSoftwareMicrosoftCommand ProcessorCompletionChar
    HKEY_CURRENT_USERSoftwareMicrosoftCommand ProcessorPathCompletionChar

with the hex value of a control character to use for a particular
function (e.g.  0x4 is Ctrl-D and 0x6 is Ctrl-F).  The user specific
settings take precedence over the machine settings.  The command line
switches take precedence over the registry settings.

If completion is enabled with the /F:ON switch, the two control
characters used are Ctrl-D for directory name completion and Ctrl-F for
file name completion.  To disable a particular completion character in
the registry, use the value for space (0x20) as it is not a valid
control character.

Completion is invoked when you type either of the two control
characters.  The completion function takes the path string to the left
of the cursor appends a wild card character to it if none is already
present and builds up a list of paths that match.  It then displays the
first matching path.  If no paths match, it just beeps and leaves the
display alone.  Thereafter, repeated pressing of the same control
character will cycle through the list of matching paths.  Pressing the
Shift key with the control character will move through the list
backwards.  If you edit the line in any way and press the control
character again, the saved list of matching paths is discarded and a new
one generated.  The same occurs if you switch between file and directory
name completion.  The only difference between the two control characters
is the file completion character matches both file and directory names,
while the directory completion character only matches directory names.
If file completion is used on any of the built in directory commands
(CD, MD or RD) then directory completion is assumed.

The completion code deals correctly with file names that contain spaces
or other special characters by placing quotes around the matching path.
Also, if you back up, then invoke completion from within a line, the
text to the right of the cursor at the point completion was invoked is
discarded.

The special characters that require quotes are:
     <space>
     &()[]{}^=;!' ,`~

:/>  Командная строка администратора Windows 10. Запуск CMD Windows

Iffexist.bat

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

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

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

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

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

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

Joinvars.bat

Объединяет все строчки из входного текстового файла в одну строчку (используя как разделитель точку с запятой), и присваивает её переменной.

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

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

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

Printargs.exe

Выводит на консоль свои входные аргументы с добавлением ограничивающих кавычек.

Printfile.bat

Печатает текстовой файл на экран.

Того же эффекта можно добиться и с помощью команды type:

ПРИМЕЧАНИЕ

В отличие от команды more, type не ждет подтверждения следующей страницы для вывода.

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

Setvarfromstd.bat

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

Splitvars.bat

Разбивает все строчки из входного файла/литерала, объединённых через точку с запятой, на несколько строчек и последовательно выводит их.

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

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

Unset.bat

Безопасно удаляет переменную.

Автовыполнение команд из разделов реестра «autorun»

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

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

Автозавершение имён файлов и папок

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

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

Позволяет автоматически завершать имена файлов и папок в окне консоли cmd.exe. Для использования следует ввести часть пути и нажать назначенную клавишу, соответственно, для автозавершения имени файла или папки.

По умолчанию автозавершение имен файлов и папок не используется. Можно включить или отключить завершение имен файлов и папок для всех процессов cmd.exe, задав значения для CompletionChar и PathCompletionChar. Чтобы задать значение, введите шестнадцатеричное число управляющего символа для определенной функции (например, 0x09 — это TAB, а 0x08 — это BACKSPACE). Перезапустите консоль, чтобы изменения вступили в силу.

Также автозавершение имён файлов и папок можно включить или отключить для консольного процесса cmd.exe с помощью параметра /f:{on|off}.

Внешние утилиты и скрипты

В этом разделе описаны утилиты и скрипты, прилагаемые к статье. Некоторые представленные здесь скрипты могут вызывать другие, поэтому следует рассматривать их вместе.

ПРИМЕЧАНИЕ

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

Восстанавливаем окружение после работы скрипта

Что, если требуется после работы скрипта восстановить окружение в том виде, который был до его вызова? Очевидно, это можно сделать вручную, но иногда достаточно воспользоваться для этого командами setlocal/endlocal. Кроме того, вам может понадобиться удалить переменные, созданные чужим скриптом (имена которых неизвестны).

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

ПРИМЕЧАНИЕ

К сожалению, setlocal/endlocal (с параметрами и без) работают только внутри скрипта (endlocal даже не нужно вызывать явно, после выполнения скрипта, он будет вызван столько же раз, сколько был вызван setlocal в этом же скрипте) и не работают вне его (в окне консоли).

Всё дело в скобках!

На самом деле проблема кроется не в самом операторе if, а в операторе “скобки” или, другими словами, в блоке кода, образуемом этими скобками (далее для краткости будет использоваться термин «блок»). Можно переписать вышеприведённые примеры без оператора if.

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

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

Вызов внешнего приложения

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

Результат выполнения утилиты printargs.exe с аргументами:

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

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

Если в нашем распоряжении имеется переменная, которая уже имеет какое-то ранее присвоенное значение и имя этой переменной присутствует в значении другой переменной, чтобы раскрыть значение второй переменной так, чтобы значение первой переменной подставилось вместо ее “%”-имени в значение второй переменной, можно воспользоваться синтаксисом переустановки переменной через вызов 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”:

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

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

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

ПРИМЕЧАНИЕ

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

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

:/>  Синхронизация времени в Windows 7

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

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

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

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

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

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

К сожалению, и данный способ содержит недостаток, а именно, “двойной эффект”, и, кроме “включения” оператора “!”, сохраняет все переменные в стек (см. описание команд setlocal/endlocal далее).

Есть ли замена 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». Перезапустите консоль, чтобы изменения вступили в силу.

Расширенный режим командного процессора можно также включить или отключить для консольного процесса с помощью параметра /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

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

Для запрета режима командной строки и обработки bat-файлов создайте в этом ключе параметр с именем «DisableCMD», который может принимать значения:

  • «0» (или отсутствие записи в реестре) — система может использовать режим командной строки и обрабатывать bat-файлы;
  • «1» — система не может использовать режим командной строки, но может обрабатывать bat-файлы;
  • «2» — система не может использовать режим командной строки и обрабатывать bat-файлы.
  • Указанные значения проверяются при запуске новой консоли, поэтому перезагружаться не требуется.

Ключи реестра

Приведу некоторые ключи реестра, отвечающие за взаимодействие с командным процессором – cmd.exe.

Команда 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”:

Команда set

СОВЕТ

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

Несколько слов о компиляторах

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

В Интернете вы можете найти несколько компиляторов:

Оператор “!” или оператор “%”?

Фактически, оператор “!” – ни что иное, как оператор “%”, но с меньшим приоритетом.

В следующем примере мы создаём переменную, имя которой состоит из одного символа – “!”. Так как приоритет символа “%” выше, чем “!”, то “%”-переменные раскроются до “!”-переменных.

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

В дополнение, оператор “!” не экранируем ни самим собой, ни другими конструкциями спецсимволов. В данном случае рекомендуется в качестве замещения экранированию использовать переменную со значением “!”.

Далее показывается, как можно выводить символ “!” при включённом раскрытии “!”-переменных.

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

Лишние кавычки здесь – детали вызова printargs.exe с параметрами (см. “Вызов с параметрами”).

Отложенное раскрытие переменных среды

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

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

Данный параметр эквивалентен применению команды

на “глобальном” уровне, соответственно, для текущего сеанса пользователя или для всех пользователей.

Отложенное расширение переменных среды по умолчанию не включено. Для включения используйте параметр «DelayedExpansion» со значением «1». Удалите созданный параметр или измените его значение на «0», чтобы вернуть настройки к первоначальному виду. Перезапустите консоль, чтобы изменения вступили в силу.

Также отложенное раскрытие переменных среды можно включить или отключить для консольного процесса cmd.exe с помощью параметра /v:{on|off}.

Предисловие

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

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

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

ПРИМЕЧАНИЕ

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

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

Проблема обратного слеша ()

Существует одна проблема, которая не позволяет использовать завершающий обратный слеш (т.е. символ “”) внутри параметров скриптов и приложений, вызываемых с использованием cmd.exe. В этом случае cmd.exe работает так, что завершающий обратный слеш приводит к «экранированию» кавычки, расположенной сразу за ним.

Результат выполнения утилиты printargs.exe с аргументами:

Чтобы избавиться от этого «эффекта», достаточно добавить ещё один обратный слеш.

Результат выполнения утилиты printargs.exe с аргументами:

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

Будьте осторожны при использовании встроенных переменных внутри таких аргументов. К примеру, такие переменные как %~dp0 и %~p0 всегда содержат завершающий обратный слеш, и будут приводить к тому же «эффекту».

Сброс errorlevel

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

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

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

:/>  [CMD] Как создать батник для обработки файлов определённого типа | SafeZone

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

ПРИМЕЧАНИЕ

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

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

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

Создание переменной

Рассмотрим небольшой пример:

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

Что здесь может быть неправильно, кроме того, что скрипт выводит не то, что нужно?

Что здесь произошло, можно понять после небольшого исправления.

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

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

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

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

Спецсимволы

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

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

можно экранировать с помощью символа (если не используются ограничивающие кавычки)

а внутри командной строки консоли cmd.exe – только с помощью кавычек.

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

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

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

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

Вывод скрипта “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-аргумента.

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

Из документации следует, что команды условного выполнения имеют в основном две формы. Первая форма – это условие-команда (then-переход), работающее по принципу “одно условие – одна команда”. И вторая форма – условие-блок, работающее по принципу “одно условие – несколько (блок) команд”.

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

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

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

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

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

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

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

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

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

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

Установка значения переменных из текстовых файлов

При чтении значений переменных из текстовых файлов командой “set /P”, попытка использовать формат текстового файла, отличный от формата текстовых файлов Windows (здесь и далее имеется ввиду формат перевода строк, который, в отличие от Unix-фомата, имеет два символа перевода строки вместо одного), может привести к ошибкам в точках раскрытия таких переменных.

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

В качестве решения помогает переустановка переменной сразу после её чтения из файла.

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

Appendvar.bat

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

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

Установка значения переменных из стандартного вывода

В стандартных утилитах Windows NT нет Unix-подобной утилиты printf, с помощью которой можно было бы сохранить вывод скрипта или утилиты в переменную. Тем не менее, этого можно добиться с помощью команды for.

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

Следует заметить, что для выполнения аргумента for скрипт создает новую консоль cmd.exe, в которой выполняет команду. Для проверки скрипт можно запустить так:

и проверить появление нового процесса cmd.exe в списке процессов.

Заключение

Я постарался привести здесь всё, с чем мне приходилось столкнуться при написании 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