В отличие от знаменитого героя комиксов, я не так богат и технические прибамбасы для меня разрабатывает не гений-технарь, а корпорация зла, известная своими “великолепными” программистами. Вероятно вы уже поняли, что я буду петь дифирамбы гениальности кадров Microsoft. Начну, как водится, с начала. Несмотря на мое нытьё в предыдущем посте, вселенная не сжалилась надо мною. Очередной день на службе принес ещё одну занимательную задачу. Заказчику взбрело в голову немыслимое: сделать так чтобы красиво, само собой и без проблем. И, конечно же, в условиях, диктуемых детищем упомянутой мега-конторы, пункт “само собой” обеспечивается штатными средствами, то бишь мерзким скриптовым языком встроенным в ОС, в узких кругах измученных людей известным как BATCH. Я поворошил память и оценил багаж знаний о языке. Он был весьма скуден и ограничивался командой Echo и условным исполнением через && и || . Следующие два дня “исправили” этот факт. Теперь я могу тихонько помалкивать в приличном обществе о том, что кое-что понимаю в командных файлах. Пробегусь мельком по ключевым моментам которые пригодились бы начинающему BAT-писателю, если бы он когда нибудь добрался до этой страницы. Назвать это ошибками начинающего у меня язык не поворачивается, ибо кто захочет не только начать но и продолжить общаться с таким убогим средством как командные файлы Windows. Так что назовем это распространенными граблями, которые раскидал на нашем поле Билл Гейтс лично ещё в те времена, когда не мнил себя филантропом и меценатскую деятельность в гробу видал.

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

Чтобы текст команды не отображался во время исполнения, следует указать перед командой символ @. Обычно в больших скриптах это самое отображение (именуемое в консолях Эхом) отключают полностью с помощью команды Echo off. Сама эта команда при этом отобразится, поэтому добавим перед нею упомянутую “Эт”. Получим:

@echo off

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

Пункт второй: эти забавные имена.

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

Например:

@echo off
SET var var =Вот такая вот строка!
echo var var 

Ахинея какая-то, не правда ли? Попробуйте перепечатать этот пример в блокнот. Обязательно руками. Без копипастов. Работать будет далеко не у всех. Причина в том, что посредине переменной стоит знак табуляции, который в ряде ситуаций на глаз неотличим от пробела. Кстати о пробелах: пробел в конце, перед знаком равно, он тоже является частью имени! Если наткнетесь на подобное в чужих скриптах, смело бейте автора по лицу и убегайте. Он это заслужил. Советую следовать общей практике в выборе имен переменных для BAT-файлов: только большие латинские буквы, цифры и знак подчеркивания вместо пробела. Без пробела перед равно. Как-то так:

SET MY_CORRECT_VARIABLE=Good!!!

Пункт третий: продолжая тему пробелов.

Навскидку скажите, что не так в этом примере:

echo off
set var=c:\program files
echo %var%
if exist %var%(
echo var exists!!!
echo.
)

Подсказка: две ошибки в одном месте. Ты догадался, малыш? Молодец! Возьми с полки конфетку! А пока крикливый уродец лезет за конфетами, рискуя свернуть шею, я поясню ошибки:

  1. Путь в переменной содержит пробел, поэтому после раскрытия переменной интерпретатор получает строку "if exist c:\program files(", что воспринимается им как "Если существует c:\program, выполнить files(". Во избежание этого эффекта нужно заключать переменные с путями в кавычки: "%var%".
  2. Между именем переменной и открывающей блок кода скобкой нет пробела, поэтому скобка воспринимается как часть аргумента оператора exist. Это приведет к безусловному исполнению блока кода, который заключен в скобки.

Правильный вариант кода выглядит так:

echo off
set var=c:\program files
echo %var%
if exist "%var%" (
echo var exists!!!
echo.
)

Кстати, echo с точкой служит для вывода пустой строки. Своего рода “\n”.

Пункт четвертый: интерактивность, как бы.

Ещё один кусок кода, теперь подлиннее:

@echo off
SET INSTALL_PATH=c:\program files\
:ask_again
if exist "%INSTALL_PATH%" (
SET /P PATH_EXISTS_ANSWER=Path exists, overwrite?[y/n/default:n]
if not defined PATH_EXISTS_ANSWER (
echo You chose default action^(N^). Try another installation path.
echo.
goto default
)
if /I "%PATH_EXISTS_ANSWER%"=="n" (
echo You chose not to use existing folder. Try another installation path.
echo.
goto noc
)
if /I "%PATH_EXISTS_ANSWER%"=="y" (
echo You chose to overwrite existing folder. Existing files will be overwritten.
echo.
goto yesc
)
echo Please choose Y or N
echo.
goto ask_again
)
:yesc
echo you said yes
goto end
:default
echo you said default no
goto end
:noc
echo you said no
goto end
:end

Тем кому лень разбираться поясню: суть в том чтобы реагировать на кнопки Y, N и Enter( N по-умолчанию) и, таким образом, получить от пользователя ответ, хочет ли он устанавливать программу в указанную ранее папку. И все бы было чудесно, если бы не отсутствие серого вещества у программистов ( или архитекторов) Майкрософт. В реальном мире подобный код работает и приносит пользу. Но в мире безумного гения Билли все не так просто. По какой-то невероятной причине при исполнении скрипта нажатие Y или N в ответ на вопрос игнорируется. Ну, не совсем игнорируется. После ответа на вопрос переменная PATH_EXISTS_ANSWER перестает быть неопределенной, но значение не приобретает. Говорю же, гении! “Именно так все и должно работать!” Результат: проверки на значение не проходятся и, несмотря на то, что пользователь уже дал ответ, скрипт спрашивает его ещё раз, утверждая, что пользователь должен выбрать “Y or N”. Не надо иметь семи пядей во лбу, чтобы понять: что то не так. Дальше - больше. Ответив второй раз на вопрос вы…получаете реакцию на ваш первый ответ! То есть если последовательно отвечать сначала Нет, потом Да, то вывод программы будет такой:

e:\test>cmd.exe /C 123.bat
Path exists, overwrite?[y/n/default:n]n
Please choose Y or N
Path exists, overwrite?[y/n/default:n]y
You chose not to use existing folder. Try another installation path.
you said no

Сейчас к слову были бы пара матерных выражений. Объясняется такое поведение тем, что интерпретатор вычисляет все значения которые может на этапе чтения скрипта, а потом при исполнении переопределяет их, но значение они приобретают только после первого исполнения. А исполняется код как-бы блоками, поэтому переменная установленная внутри блока получает значение, но извлечь его из переменной можно только выйдя из блока. Убедиться в этом можно на более простом примере:

@echo off
SET INSTALL_PATH=c:\program files\
if exist c:\ (
SET var=valueOne
echo %var%
)
echo %var%

Даже тут видно, что первый оператор Echo не выводит результата, хотя тут же ясно сказано “SET var=valueOne”! Видимо авторы интерпретатора понимали несовершенство их поделки и поэтому придумали обходной путь. Назвали его “Отложенным раскрытием переменных”. Чтобы задействовать его надо в следующей после Echo off строке написать: SETLOCAL EnableDelayedExpansion. Тогда переменные будут пересчитываться во время исполнения( Прямо как в нормальных языках! Представляете?!). Но это не все! Переменные пересчитываются не сами собой, а при обращении к ним с помощью специального синтаксиса. Для этого при получении значения переменной нужно использовать не %, как мы все привыкли, а знак восклицания. Переделаем код последнего примера:

@echo off
SETLOCAL EnableDelayedExpansion
SET INSTALL_PATH=c:\program files\
if exist c:\ (
SET var=valueOne
echo %var%
echo !var!
echo %var%
)
echo %var%

Вывод этого скрипта:

e:\test>cmd.exe /C 123.bat
ECHO is off.
valueOne
ECHO is off.
valueOne

Ужас, не так ли? Я конечно не видел исходников интерпретатора, но мне почему то кажется, что значение пересчитанное один раз можно просто хранить. То есть после обращения к !var! можно было бы сохранить полученное значение в переменной var так, чтобы второе обращение к ней через % уже выдавало бы значение, а не “ECHO is off.”. Впрочем, не претендую на мнение эксперта. Что вижу, о том и пою. Мне кажется это логичным.

На этом мои силы иссякли. Microsoft безусловно велик, но никто не будет отрицать, что можно было сделать лучше где-то около 99% их продуктов. Самый безупречный из них - Notepad. Да и тот не имеет подсветки синтаксиса =).

Мне остается только пожелать удачи тем кто впервые ступил на это широкое, но невероятно плотно заминированное поле командных файлов. Да прибудут с вами терпение и стальные нервы!

Это Александр и мой блог о жутких скриптовых языках.