Программизм
 
Программизм
На главную | Графомания | Программизм | Книги | Всячина | Скачать | Ъ?  

Batch-файлы в Windows NT, часть 2

Посвящается Сергею Г. и всем остальным, у кого заклинание RTFM не выбито золотом по чёрному над рабочим местом.

Сразу предупреждаю, что практически весь материал, приведённый здесь, можно найти на своём компьютере, выдав в командной строке несколько нехитрых команд:

if /?
for /?
set /?
call /?

Мало того, если нажать кнопку Пуск, затем Справка, а потом в Справочных сведениях найти Главную страницу справочника по командам, можно узнать ещё больше интересного. Пытливый же ум может прочитать ещё и учебник по командному процессору 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 — всё ещё корявый и хитрый, но уже достаточно мощный инструмент, которым можно и нужно пользоваться в повседневной жизни. Успехов вам, товарищи!

10.01.2003


* Здесь и далее стихи Андрея Макаревича и немного мои.
Поиск
См. также

Командный язык Windows NT — всё ещё корявый и хитрый, но уже достаточно мощный инструмент, которым можно и нужно пользоваться... »»»

Контрольный вопрос: какая из компонент аппаратуры сильнее всего влияет на скорость исполнения приложений?... »»»

каждые три восьмибитных байта кодируются четырьмя шестибитными байтами. Вопрос в том, как выбрать диапазон печатных символов, чтобы отобразить множество полученных шестибитных чисел... »»»

Рекомендую

e.g.Orius’
Игорь Иртеньев
Вячеслав Шевченко

Copyright notice

ъ) Все материалы, размещённые на странице, являются неотъемлемой собственностью автора с вытекающими отсюда правами, как ©, так и (ъ). Некоммерческое их распространение всячески приветствуется, разумеется, при условии сохранения ссылки на оригинал. Что касается коммерческого использования — пишите письма, договориться можно всегда.

Удивительное рядом

lj userhardsign
Закладки Карта Королёва

Пишите письма

Счётчики

XPEHOMETP™ Рейтинг@Mail.ru