Источник:
1 |
http://www.xakep.ru/magazine/xs/021/068/1.asp |
Описание:
1 2 3 4 5 6 |
Мы займемся составлением полезных командных файлов, по традиции зовущихся батниками (хотя на смену ".bat" давно пришел ".cmd"). Язык командных файлов состоит из двух больших частей: выполняемые команды и системные метки. Каждой из выполняемых команд соответствует одноименный экзешник ("ping" - c:\WINNT\system32\ping.exe, "find" - C:\WINNT\system32\find.exe), метки же просто распознаются системой в контексте батника (циклы, условные операторы). Можно сказать, что команд вообще не существует :), просто средствами языка вызываются нужные программы. Так оно и есть, но поскольку круг чисто служебных прог ограничен (не будешь же ты запускать тот же sleep не из батника), обзовем их командами. |
вывод:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Самые простые операторы - операторы вывода. В батниках для этого используется команда "echo". Для вывода на экран строки "Klya is a greatest DUM" в CMD-файле надо прописать: echo Klya is a greatest DUM Только вот незадача - батники дублируют выполняемые команды, чтобы ты видел, чего, собственно, у них внутри творится. В нашем случае вместо одной строки на экране появится: echo Klya is a greatest DUM Klya is a greatest DUM Бороться с дублированием можно по-разному: для отключения вывода одной команды перед ней ставится "@", для отключения всех последующих надо дать команду "echo off". Так как обычно дублирующий вывод никому не нужен, в начале файла прописывают: @echo off - эта строка отключает дальнейший вывод и в придачу скрывает сама себя. |
Условия:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Условный оператор, как и следовало ожидать, обзывается IF'ом и имеет такой формат: if [строка] [условие сравнения] [строка] [команда] В качестве условий сравнения строк применимы такие сокращения: EQU - равно, NEQ - не равно, LSS - меньше, GTR - больше, LEQ - меньше или равно, GEQ - больше или равно. Если сразу после IF поставить ключ /i, сравнялке будет наплевать на регистр строк (то есть А=а, B=b и т.д.). Существуют еще два подвида условных операторов: "if errorlevel [индекс ошибки]" и "if exists [имя файла]". Первый нужен для обработки критических ситуаций (выяснить, что за ошибка произошла). Во времена ДОСа это условие реально пользовали только для проверки файла на существование. К 2000 году новый подвид IF'a - "if exists", который только и делает, что проверяет существование файла. |
Вызов:
1 2 3 4 5 6 7 8 9 10 |
Для вызова других командных файлов в батниках можно использовать аж три команды. Во-первых, можно просто написать имя другого батника в строке. При таком вызове выполнение текущего завершится, а вызванный пойдет на исполнение. Во-вторых, можно вызвать батник командой "call [имя батника]". Тогда выполнение текущего CMD-файла приостановится до завершения работы вызванного, а потом снова возобновится. В-третьих, другой батник или просто команду можно запустить на выполнение параллельно с текущим командой "start [заголовок окна] [имя батника\команды]". Тогда новый скрипт откроется в новом окне (кстати, если добавить к команде параметр /wait, то текущий батник будет ждать завершения вызванного). |
Метки:
1 2 3 4 5 6 7 8 9 10 11 12 |
Сколько нам твердили, что программирование с GOTO - отстой... Здесь г. Гейтс не оставляет нам шансов на альтернативу, придется юзать. Метки оформляются так: :metka goto metka Любую метку можно вызвать командой call, делается это так: call :metka Тогда весь текст батника от метки и до конца будет считаться новым батником :). Заморочено, но удобно. |
Без метки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Настоящие программисты не любят метки, потому что из-за них может возникнуть недетское зацикливание. Но именно поэтому метки нравятся нам - западлостроителям. Самое простейшее зло на командном языке выглядит примерно так: ----klya.cmd---- @echo off :dr start klya.cmd goto dr ----klya.cmd---- Этот зло-скриптик будет заниматься вызовом самого себя бесконечное число раз. Причем каждый вызванный им экземпляр будет заниматься тем же. Скорость ухода тачки в даун зависит только от ее мощности. До скорого ребута! |
Параметры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
Помнят ли потомки ДОСа славное начинание по передаче аргументов прямо из командной строки? Помнят, да еще как. Начиная с НТшки, аргументы можно делить на части и преобразовывать, не отходя от кассы. Обращения к параметрам проходят вот так: %1 - просто первый аргумент %* - все аргументы %~1 - первый аргумент с удалением кавычек ("колбаса" > колбаса) Остальные параметры действуют, если только батнику передается имя файла (вызов типа klya.cmd dr.txt). %~f1 - абсолютный путь к файлу %~d1 - имя диска %~p1 - имя каталога %~n1 - имя файла (klya.txt > klya) %~x1 - расширение файла (klya.txt > txt) %~a1 - атрибуты файла %~t1 - дата и время создания файла %~s1 - размер файла Все параметры комбинируются без проблем: %~dpnxs1 выдаст поделенный на части путь к файлу и его размер, и т.п. |
Пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
Так как теория лучше всего познается на практике, попробуем написать небольшой файлик, который будет... Ну, скажем, будем помещать копии файла во все подкаталоги текущего каталога. Типа ты админ и раздаешь всем пользователям новые правила работы :). Подобный файл разбирается на microsoft.com, но его можно несколько улучшить. В нем будут присутствовать циклы, в следующий раз мы разберем их подробнее, а пока постарайся разобраться в том, что есть. Наш батник будет вызываться так: admin rules.txt subdir. Если не указано имя директории, будем работать в текущей. @echo off rem Это комментарии :) if (%1)==() goto :bad if (%2)==() goto :curdir rem Проверяем, заданы ли параметры в командной строке. Если нет файла, сваливаем на :bad. for /F "skip=7 tokens=4" %%a in ('dir %2 /ad') do call :copyall %%a %1 %2 rem Этот дикий цикл означает: из вывода команды "dir %2 /ad" выдирать слова после четвертого знака табуляции (это будут имена), класть их в переменную %%а и передавать подпроцедуре "copyall". При этом первые семь строк вывода надо пропустить (там идет служебная инфа). goto :eof :curdir for /F "skip=7 tokens=4" %%a in ('dir /ad') do call :copyall %%a %1 . rem Тот же цикл, но мы не заморачиваемся с именем директории, считая ее текущей. goto :eof rem Ниже идет подпроцедура. В ней мы проверяем переданные имена директорий (не пустые ли), а также отсеиваем строки "столько-то bytes free". Затем выполняется процедура копирования файла (второй параметр для этой подпроцедуры) в поддиректорию заданной директории (первый и третий параметры). :copyall if (%1)==() goto :eof if (%1)==(bytes) :eof copy %2 %3\%1 goto :eof :bad echo А чего копируем-то? goto :eof Простенько и почти бесполезно :). |
Циклы FOR:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
FOR %%i IN (*.txt) DO command %%i %%i - это переменная цикла, в нее по очереди подставляются значения из набора, указанного после IN. В наборе простого цикла содержится маска или список имен файлов, для каждого из которых выполняется некоторая команда. Чтобы команда взаимодействовала с файлом, необходимо прописать переменную на место параметра команды, отвечающего за файл (в основном этот параметр первый). Простенький пример: откроем все CMD-файлы в текущей директории для правки в новом окне. FOR %%i IN (.\*.cmd) DO start edit.com %%i Таким простейшим циклом уже можно выполнять тучу полезных вещей, но мы пойдем дальше - в MS для нас придумали циклы с параметрами. Поглядим на них по порядку. FOR /D %%i IN (win*) DO command %%i Этот цикл будет выполнять команды для директорий, а не для файлов, соответственно после IN указывается список директорий (в этом примере - все, начинающиеся с WIN). FOR /R C:\WINNT\ %%i IN (win*) DO command %%i Такой цикл будет искать файлы, начинающиеся с WIN во всех подкаталогах C:\WINNT\, и выполнять команду для каждого из них. FOR /L %%i IN (6,1,10) DO command %%i При составлении отчетов для вывода на экран или генерации файлов этот цикл незаменим. В нашем примере переменная %%i будет принимать значения от тройки до десятки с шагом в единицу (6, 7, 8, 9, 10). Шаг, кстати, легко может быть отрицательным, равно как и оба значения - начальное и конечное. Правда, пригодится вряд ли ;). FOR /F ["параметры"] %%i IN ([список файлов] или ["строка"] или ['команда']) DO command %%i Самый страшный и самый функциональный цикл :). Он открывает файлы, обрабатывает в них строки с заданными параметрами и выполняет команду для слов из каждой подходящей строки (по дефолту словом считаются символы, отделенные от остатка строки пробелами или табуляцией). Его мы применяли в прошлый раз, сегодня же рассмотрим во всей красе. Ты, видимо, уже заметил, что вместо обычной маски в IN'e этого цикла стоят аж три параметра на выбор. Список файлов - он и в Африке список файлов, а вот "строка" и 'команда' - вещи для нас новые. Если параметр IN'a указан в двойных кавычках, система не интерпретирует его как список файлов или маску, а ищет слова прямо в нем. Если же параметр стоит в одинарных кавычках - он воспринимается как команда, и слова ищутся в строках, выведенных этой командой на экран (если помнишь, прошлый раз мы использовали dir для получения имен директорий). В списке параметров указываются правила обработки строки. Если не указывать параметров вообще, цикл загребет из каждой строки каждого файла только первое слово. Если это тебя не устраивает, можно прописать следующие правила отделения строк: eol=; - задает символ конца строки (после которого остаток строки пропускается), в нашем случае - ";". skip=7 - число строк от начала файла, которые надо пропустить, у нас - 7 штук. delims= , - задает символы, считающиеся разделителями слов, взамен пробела и таба, мы оставляем пробел и запятую. tokens=1,3,6-8 - жуткий параметр - задает слова для обработки, в нашем случае для каждой строки на обработку пойдут первое и третье, а также с шестого по восьмое. Если в конце параметра поставить "*" (tokens=1,3,5*), то слова будут обрабатываться с последнего указанного (5) и до конца. Самое главное - для каждого из слов строки выделяется отдельная переменная. То есть если первое слово задано как %%i, все последующие будут называться %%j, %%k, %%l и так далее по возрастанию алфавита. Нетрудно посчитать, что максимальное количество слов в строке - 52 (два алфавита: a-z, A-Z). usebackq - меняет смысл кавычек - апострофы окружают команду, одинарные кавычки - строку, а двойные - набор файлов. |
Хулиганство:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
Давай замутим какой-нибудь пример, устраивающий сладкую жизнь нашим врагам. Создадим для каждого файла из C:\WINNT\ три дубля с расширением LOH, BAK и LAN :) в папке C:\WINNT\BACKUP\. Поднапряжем, так сказать, NTFS'ку :). @echo off pushd C:\WINNT\ rem Этой командой мы сохранили текущую директорию и перешли в C:\WINNT\. echo loh > tmp.tmp echo bak >> tmp.tmp echo lan >> tmp.tmp rem Скидываем во временный файл три гадких расширения. for %%a in (*.*) do call :makebadthing %%a rem Тело цикла - для всех файлов вызываем подпроцедуру makebadthing c параметром "полное имя файла". del tmp.tmp >NUL popd goto :eof rem Конец :). Удаляем временный файл, возвращаемся в стартовый каталог. :makebadthing for /f %%b in (tmp.tmp) do copy %1 C:\WINNT\BACKUP\%~n1.%%b goto :eof rem Содержание процедуры: для каждой строки файла tmp.tmp выполняем такую команду: "скопировать переданный параметр (%1 ака %%а) в файл C:\WINNT\BACKUP\[имя старого файла].[переменная цикла]". То есть имя нового файла складывается из имени старого (%~n1) и выбранного из файла расширения (%%b). Все :). Вроде небольшое хулиганство, а счастья - полные штаны. Если прописать такой батник в автозагрузку, то через пару дней места на диске с виндами не будет вообще :). |
Command Extentions:
1 2 3 4 5 6 |
Command Extentions по дефолту включены далеко не везде, так что если ты хочешь использовать возможности CMD-интерфейса по полной программе, придется его включить :). Делается это тремя способами: ввести в командной строке CMD /X; присвоить значению ключа HKEY_CURRENT_USER\Software\Microsoft\Command Processor\EnableExtensions единицу; первой (или второй, после @echo off) строкой батника ввести SETLOCAL ENABLEEXTENSIONS. Выбирай на вкус :). |