Мало того, если нажать кнопку Пуск, затем Справка, а потом в Справочных сведениях найти Главную страницу справочника по командам, можно узнать ещё больше интересного. Пытливый же ум может прочитать ещё и учебник по командному процессору Windows NT. Здесь же речь пойдёт о скриптах, которые облегчают жизнь лично мне. Естественно, с краткими комментариями. Итак,
Поиск фильмов в корпоративной сети
Обратите внимание, в командных файлах запрещено разбивать команду на несколько строк. В этом листинге и далее если строка начинается с пробелов, то она на самом деле является продолжением предыдущей. Сделано это с единственной целью — уместиться в окно броузера.
@echo off
ver|find "Версия" >nul
if %ERRORLEVEL%==0 set disk=Диск& goto version_found
ver|find "Version" >nul
if %ERRORLEVEL%==0 set disk=Disk& goto version_found
echo Sorry, unknown language. Bye.
goto :EOF
:version_found
for /F "tokens=1 delims= " %%m in ('net view^|find "\\"') do
call :invsrv %%m
goto :EOF
:invsrv
for /F "tokens=1" %%d in ('net view %1^|find "%disk%"') do
call :invdir %1\%%d
goto :EOF
:invdir
dir /b /s "%1\*.avi" "%1\*.mpg" "%1\*.mpeg" 2>nul
goto :EOF
Мы в такие шагали дали,
Что не очень-то и дойдёшь,
Мы такие скрипты писали,
Что не очень-то и поймёшь,
Мы с почтеньем относимся к данным,
Правда, с кодом часто мудрим...
Мы охотники за экраном —
Дампом цвета ультрамарин*.
С первой строчкой всё должно быть более-менее понятно.
Вторая группа предназначена для определения версии системы — русская или английская. К сожалению, от этого зависит вывод некоторых команд, в частности, net . Команда find похожа на grep . У неё не столь широкие возможности, зато она входит в стандартную поставку. Errorlevel , в отличие от DOS, можно не только проверять на «больше или равен», но и использовать как переменную в полноценных операциях сравнения. Символ & разделяет несколько команд в строке, причём вторая выполнится независимо от того, с каким результатом завершилась первая. Есть, кроме того, разделители && и || , которые выполняют вторую команду только при успешном/неуспешном завершении первой. Обратите внимание, что разделитель стоит вплотную к значению переменной, иначе в значение добавляется дополнительный пробел.
:EOF — это виртуальная метка, которая стоит в конце файла. Переход на неё обозначает окончание выполнения. В отличие от exit , такой способ не завершает командный интерпретатор.
Следующая группа команд представляет собой цикл по всем компьютерам сети. Мы получаем список командой net view , затем фильтруем его, чтобы выкинуть служебную информацию, а имена компьютеров оставить. Обратите внимание на символ ^ перед разделителем | — он позволяет интерпретировать всё, что написано внутри апострофов, как одну строку. Иначе этот разделитель воспринимался бы буквально, то есть разбил бы прямо посередине объемлющую команду — for .
for с ключом /f воспринимает не список значений, а поток, из которого эти значения читаются. Если название потока заключено в апострофы, как в нашем примере, то поток представляет собой стандартный вывод записанной там команды. Параметр tokens позволяет записывать в переменную цикла не целиком строку, а только первое слово из строки. Слова разделяются символами, указанными после delims.
Для каждого компьютера вызывается подпрограмма с именем invsrv . Если перед именем подпрограммы стоит двоеточие, это сигнал, что подпрограмма является не внешней программой, а меткой в текущем файле. Окончанием подпрограммы считается выполнение последней инструкции файла или, что одно и то же, переход по метке :EOF .
Дальше для каждого компьютера читается список открытых каталогов, а для каждого каталога вызывается команда dir — используются уже описанные ранее конструкции. Символ 2>nul подавляет сообщения об ошибках (поток stderr ).
Говорят, что на эту тему
Наложили давно запрет,
Будто в коде нашей системы
Этих багов в помине нет,
Говорят, что в Windows 2000
Их исправили навсегда,
Только я говорю: «Дружище,
Это полная ерунда!»
Есть ли недостатки у данного скрипта? Безусловно. Например, он не будет работать для открытых каталогов, в именах которых содержится пробел. Можно исправить это, написав во втором цикле tokens=* , а вместо простого net view — маленький фильтр, например, на gawk (не забывайте, что строки на самом деле не разрываются):
net view %1 | gawk "
/Disk/{
l=\"\";
for(i=1;$i!=\"Disk\";i++) l=l \" \" $i;
print l;
}"
Для английской Windows это будет работать, а вот для русской — увы. Кстати, не забудьте, что все русские буквы в приведённом выше скрипте — в кодировке OEM, т.е. CP-866.
Приятного просмотра!
Архивирование модифицированных файлов
Иногда требуется создать архив, куда попали бы свежеисправленные файлы. Как правило, эта задача решается тривиально — при изменении файла у него автоматически устанавливается атрибут Archive, а потом архиватор архивирует файлы с установленным атрибутом, а сам атрибут сбрасывает. И вдруг оказалось, что InfoZip этого не умеет!
Никто не собирается спорить с тем, что Rar сжимает файлы лучше, а PkZip, возможно, понимает атрибуты. Просто для закачки, например, файлов по http на этот сайт архив обязательно должен быть zipом. А PkZip, что бы там ни говорили отдельные товарищи, программа коммерческая, т.е. не бесплатная.
Не стоит отчаиваться, не всё так плохо. InfoZip умеет добавлять в архив файлы, измененные не раньше определённой даты. Единственное, что надо учесть, что в России принято писать дату как ДД.ММ.ГГГГ, а в Америке — MM.DD.YYYY
Имея на руках вышеприведённые сведения, пытливый ум напишет что-нибудь вроде следующего:
@echo off
del /f site.zip >nul 2>nul
for /f "tokens=2,3,4 delims=. " %%i in ('date /t') do
set curdate=%%j%%i%%k
zip -9Xr -t %curdate% site * -x update.cmd
и будет абсолютно прав.
Нумерация файлов в каталоге
Ещё одна задача. В каталоге имеется куча файлов, которые надо переименовать так, чтобы номера были последовательными числами. В качестве параметра скрипту будем передавать маску файлов и первый номер. Изучив вышеприведённые примеры, любой неленивый программист после некоторого раздумья напишет:
@echo off
if .%1==. goto usage
set n=%2
if .%n%==. set n=1
set tempdir=$tmp$.$x$
mkdir %tempdir%
for %%i in (%1) do call :renfile %%i
move %tempdir%\%1 . >nul
rmdir %tempdir%
goto :EOF
:renfile
move %1 %tempdir%\%n%%~x1 > nul
set /a n+=1
goto :EOF
:usage
echo USAGE RENUMBER *.HTM ^<first number^>
Потом, правда, возникнет вопрос, нельзя ли вместо вызова подпрограммы renfile сразу через амперсанд написать требуемые команды. Но, подумав или попробовав, можно убедиться, что нельзя, т.к. неправильно будет работать set . Дело в том, что подстановка значения переменной происходит до, а не во время выполнения цикла, т.е. сначала вычислится %tempdir%\%n%%~x1 , а потом уже все файлы будут перемещаться по одному и тому же адресу.
Правда, можно сделать и так, чтобы значения переменных подставлялись при каждой итерации цикла. Для этого надо заключить переменную не в проценты, а в восклицательные знаки, то есть написать %tempdir%\!n!%~x1 . А потом ещё разрешить такую обработку переменных, запустив командный процессор с ключом /V:ON . Можно и разрешить такую обработку по умолчанию, записав 1 по одному из следующих адресов в реестре (для одного пользователя и для всех пользователей соответственно):
HKCU\Software\Microsoft\Command Processor\DelayedExpansion
HKLM\Software\Microsoft\Command Processor\DelayedExpansion
Кстати, вот ещё одна нехитрая команда, которая позволит узнать больше о внутреннем мире вашего компьютера:
cmd /?
Злые баги не остановишь,
Просто знайте: blue screen of death
То ли Гейтс, то ли Руссинович
Перекрасили в чёрный цвет
И пришлось им забраться глубже —
Прямо в код самого ядра...
Чтоб поймать их, работать нужно
Просто с вечера до утра.
Из особенностей кода хотелось бы отметить присваивание с вычислением (set /a ) и выделение из полного имени файла расширения (конструкция %~x1 ). Подробнее о подобных конструкциях см. call /? . Ну, и разумеется, птички перед символами < и > , позволяющие не воспринимать их как команды перенаправления ввода/вывода.
Узнать длину файла
Задача сформулирована предельно просто. Проигнорировать файл, если его не существует или он нулевой длины (или ненулевой). Тот, кто детально разобрался с инструкцией for , радостно воскликнет: «Давайте напишем подпрограмму, вычисляющую её! Например, вот так...»
:list_file
for /f "tokens=3" %%i in ('dir /-C %1^|find "%1"') do
set FILELEN=%%i
goto :EOF
Нельзя не согласиться с товарищем, если ничего, кроме Windows NT 4 он не видел. Но настоящий программист, у которого под рукой Windows 2000 и которого данная статья побудила RTFM, поступит так:
:list_file
set FILELEN=%~z1
goto :EOF
Выводы
Командный язык Windows NT — всё ещё корявый и хитрый, но уже достаточно мощный инструмент, которым можно и нужно пользоваться в повседневной жизни. Успехов вам, товарищи!
Источник: http://hardsign.hardsign.com/prog/2 |