Давайте заглянем в help по команде FOR.
Есть 4 нотации синтаксиса этого оператора:
Запоминатать не надо, просто обратите внимание, что с директивой usebackq изменилась обработка кавычек, фактически их низвергли до стандартных символов. Это удобно, и позволяет городить вот такие конструкции:
for /f "usebackq delims=*" %%a in (`""%~dp0Tools\findstr.exe" /c:. /r "%EnvFile%""`) do echo "%%a"
и все для того, чтобы нормально передавались пробелы в пути до файла описанного переменнной %EnvFile%.
Ну да ладно, я отвлекся.
Стандартная запись: FOR %variable IN (set) DO command [command-parameters]
Мы говорим ДЛЯ какой переменной, ИЗ какого набора, ЧТО делаем.
И тут все просто:
cd System32
for %a in (????.sys) do echo %a
Логика работы команды FOR во всех режимах одинакова.
for %a in ("%Temp%") do echo %a - аналог "echo %Temp%", только сложнее.
for %a in (*.sys) do echo %a %~aa %~ta %~za
Едем дальше, потому что сейчас будет само интересное.
FOR /F
Давайте подробно остановимся на содержимом скобок, это ключевое и важное для понимая знание:
Стандартный вид: | |
file-set | Набор файлов. Маска,по которой надо выбрать набор файлов, который обрабатывается опциями и отдается на исполнение. |
"string" | Строка. Это не маска файлов. Это строка, которая обрабатывается опциями и отдается на исполнение. |
'command' | Это Shell команда, как Dir, например, результат которой будет построчно обрабатан в соответствии с опциями и отдан на исполнение. |
Директива в options: "usebackq" кое что меняет в синтаксисе. | |
file-set | Набор файлов. Все тоже самое, как классическом в режиме. |
'string' | Строка. Если в строке присуствуют двойные кавычки, самое лучшее решение это useback и пробелмы в сторону. |
`command` | Shell команда. Если в команде есть двойные кавычки, то это самый подходящй выход. |
Options | |
eol=c | Указывает ОДИН символ, c которого начинается комментарий. Весьма удобно, если при разборе файла есть строки с комментариями. Символ комментирования будет приводить к пропуску строки, если она будет начинаться с него. |
skip=n | Количество строк, которые надо пропустить, перед началом разбора. Полезная штука при выбрасывании всяких заголовков и шапок выводимых некоторыми программами, например, стандартным FIND или командой DIR. |
delims=xxx | Перечисляем все символы, которые мы хотим использовать в качестве разделителей. Обычно это табуляция и пробел. |
tokens=x,y,m-n,* | Токен - это временная переменная, которая инициализируется результатом разбора строки. Можно перечислить, номера токенов, которые будут переданы на исполнение, можно указать период от и до какого токена надо передать, и если есть токен "*", то порождается дополнительный токен, который инициализируется, оставшейся не разобранной строкой. |
usebackq | Команда изменяющая обработку одиночных и двоичных кавычек в круглых скобках |
Стандартный режим. Command Extension выключен | |||||||||||||
ERRORLEVEL number | Любая программа, после окончания работы, возвращает числовое значение характеризующее код возврата. Обычно это число:
0 - если программа отработала успешно код ошибки - если у программы, что-то не получилось сделать. ----- Как такового стандарта на коды ошибок нет. Поэтому рекомендую для каждой утилиты прицениваться, какие коды и в каких случаях возвращаются. if ERRORLEVEL 0 goto ProgOk if not ERRORLEVEL 0 goto ProgBad | ||||||||||||
string1==string2 | Две строки сравниваются: Если строки содержит числа, то числа преобразуются к числу и эти два числа сравниваются друг с другом на равенство. Если преобразование к числу спотыкается, то сравнение происходит посимвольно и чувствительно к регистру | ||||||||||||
EXIST filename | Если файл или папка с именем "filename" существует.
Стоит помнить, что имена "." и ".." также являются именами файлов, которые обозначают текущую папку и родительскую папку. if not exist .. echo This is root directory. if exist .\*.sys echo .sys files presents | ||||||||||||
Если перед этой командой поставить префикс NOT, то все условия будут работать наоборот. | |||||||||||||
Расширенный режим. Command Extension включен. По умолчанию он всегда включен.
Некоторые команды начинают обрабатываться иначе. | |||||||||||||
[/I] string1 compare-op string2 | Логика такая же, сначала к числам преобразуем, если не получается, то сравниваем строки.
Префикс /I требует от команды сравнивать строки не обращая внимание на регистр. Иными словами, "ABC" будет равно "abc". Compare-op может быть одним из следующих операторов:
| ||||||||||||
DEFINED | Проверяет на наличие переменной среды. |
Вот почти и все. Остальные мелочи, изучим по ходу.
Давайте подсчитаем объем файлов в нашей экспериментально-учениеческой папке.
Самое простое это вызвать команду Dir и посмотреть на предпоследнюю строку, которая описывает число файлов.
Давайте так и сделаем.
Чтобы нам не мешался разделитель сотен и тысяч, мы будем вызывать команду DIR с ключом /-c.
Попробуйте вызывать
dir
А потом
dir /-c.
Теперь попробуем сделать это через команду FOR.
Наша задача выделить количество файлов и занимаемый ими объем.
Чтобы наш код был не зависим от используемой локализации, то я постараюсь избегать решений, явно работающих со сравнением каких либо строк.
Давайте внимательно посмотрим на вывод команды что мы видим в искомой строке ?
Она начинается с числа, потом идет указание, что это число означает, потом суммарный объем.
Если мы посмотрим внмательно, то связка "число строка, число" попадается только 1 раз. Поэтому, если мы будем выяснять является ли токен числом, то мы сможем выбрать нужную нам строку.
У shell есть предел измерения - это число 2147483647, в шестнадцатиричной форме это 0x7FFFFFFF, числа знаковые, поэтому диапазон измерений от -2147483646 до 2147483647. Чисел в понятии shell больше 2147483647 не существует.
В этом легко убедиться посмотрите на результат работы команд:
if 2147483647 equ 2147483647 echo Равно
if 2147483647 equ 2147483648 echo Равно
if 2147483647 equ 2147483646 echo Равно
if 2147483649 GTR 2147483647 echo ...49 больше ...47
Как видно все числа больше 2147483647 оказываются равны 2147483647.
Что нам это дает ?
А то, что никакое число не может быть больше 2147483647. И поэтому если мы сравниваем конструкцией GTR, то никогда не должны получить выполнение условия, если сравниваем с максимальным числом. Не существует числа больше, чем самое большое.
Однако, если мы будем сравнивать не число, то сравнение будет строковым и посимвольным и тогда
if 20.02.2004 GTR 2147483647 echo Больше
if 21.02.2004 GTR 2147483647 echo Больше
if 22.02.2004 GTR 2147483647 echo Больше
странное поведение, да ?
На самом деле в первой строке "20" из "20.02.2004" сравнивается с "21" из "2147483647", во второй "21" с "21" и в третьей "22" с "21". Это можно использовать, но будет громозко и работать будет, как захочет.
Наша первая конструкция на основе "for" кажется более очевидной.
Замечание! Есть такое: стиль программирования, так вот я часто работаю с кавычками, поэтому, чтобы сэкономить время на понимании, где надо мне применять модификатор "usebackq", а где нет, я вместо этого usebackq применяю всегда, это мой стиль программирования.
for /f "usebackq tokens=1-4 skip=5" %a in (`dir /-c`) do echo a=%a b=%b c=%c d=%d
Смотрим на результат, вот как разбирается строка на параметры, мы задали параметр "a", и опиcали, что нам понадобятся токены с 1 по 3ий, и for для нас автоматически создала описанные нами токены: 1ый токен это %a, 2ой %b и 3ий %c, итого, как просили: с 1го по 3ий.
Но допустим, а нас имя файла содержит проблелы, тогда оно будет разобрано не правильно, так как часть имени попадет в параметр "%d" , а остальное вообще потеряется.
В общем случае это не хорошо, лучше не терять, можно не обработать или пропустить, но луше не терять.
Поэтому перепишем нашу команду, так, чтобы она ничего не теряла
for /f "usebackq tokens=1,2,3,4* skip=5" %a in (`dir /-c`) do echo a=%a b=%b c=%c d=%d e=%e
Вот так лучше.
Теперь нам надо выделить последние две строки из всего объема информации.
Можно "нахимичить" применяя IF, сравнивая числа, проверяя на точки, на логическую совместимость параметров и в конце концов, 2мя, 3мя последовательными сравнениями однозначно отсеивать даты, не теряя числа. Но это не красиво и не однозначно.
Разумнее зацепиться за точки в дате, которые точно присуствуют в не нужных нам строках.
Для этого их надо найти. Поиском занимается в утилиты: FIND и FINDSTR. Последняя умеет выводить строки НЕ содержащие поисковый параметр.
Поэтому, если мы каким-нибудь образом, результат команды подадим на вход Findstr, то часть задачи мы выполним.
Можно так:
dir /-c >list.txt - сохраним результат работы команды dir /-c в файл "list.txt", а потом вызовем команду
findstr /v /c:. list.txt
Но во первых это неудобно, во вторых, требует необходимости, где-то сохранять файл, а у нас может не оказаться достаточных прав на его создание, ну и в третьих, это громозко и ко всем прочему, не в одну строчку.
Поэтому сделаем иначе, вспомним о перенаправлении ввода через поток.
dir /-c | findstr /c:. /v
Ключевым моментом здесь является символ перенаправления ввода "|".
Результат отработки команды dir вместо экрана направляется на вход команды "findstr", а та уже за неимением команд перенаправления выводит на экран результат деятельности.
Можно усложнить и отфильтровать вообще все, кроме того, что нужно
dir /-c | findstr /c:. /v | findstr /i /v /c:" c " /c:- /c::
перенаправляя вывод вместо экрана на вход второй команды "findstr /i /v /c:" c " /c:- /c::", мы отбрасываем все, что выводит лишнего команда "dir".
/c:" c " | Поиск всех строк содержащих " с ", то есть "c" окруженный пробелами. Это для фильтрации первой строки, выводимой командой dir |
/c:- | Отфильтровываем все строки содержащие симво минус. Это для фильтрации серийного номера. |
/c:: | Отфильтровываем все строки содержащие двоеточие. Это для фильтрации вывода текущей папки для которой исполняется dir |
Путь возможно верный, но не красивый, громозкий и что самое главное, не универсальный, потому что если все будет происходить на диске D:, то /c:" c " не отработает, потому что dir нам покажет строку " d ".
Шаг влево, шаг вправо, грозит сбой. Ну и к тому-же если мы можем сразу пропустить первые 5 строк, то зачем нам надо их фильтровать столь сложным образом ?
Поэтому, возвращаемся к нашей заготовке: for /f "usebackq tokens=1,2,3,4* skip=5" %a in (`dir /-c`) do echo a=%a b=%b c=%c d=%d e=%e и модифицируем ее согласно полученным знаниям, а именно, возьмем от вывода команды dir, только то, что не содержит "." точки пропустив, первые 5 строк.
Так как мы будем использовать команду переопределения вывода, то строку в круглых скобках придется взять в кавычки, однозначно обозначив, таким образом, интерпретатору команду, которую надо выполнить целиком.
for /f "usebackq tokens=1,2,3,4* skip=5" %a in (`"dir /-c | findstr /v /c:."`) do echo a=%a b=%b c=%c d=%d e=%e
Согласитесь, коротко и ясно. Как по написанию, так и по результату.
Всего две строки.
Теперь нам надо взять отсеять одну от другой. Посмотрим, чем отличается одна от другой ? Вторая разбилась на большее количество параметров, чем первая. То есть в ней больше групп символов, которые можно разобрать и раскидать по токенам. Наша строка имеет неопределенным 5ый токен. За это и зацепимся.
IF - не может сравнивать пустые строки. Он выдает ошибку, что приводит к завершению выполнения блока команд или всего пакетного файла.
Поэтому воспользуемся простым способом сравнения, путем добавления лишнего символа к сравниваемой строке.
If то_что_может_оказаться_пустым.==. - Добавляя точку к концу или к началу сравниваемой строки, мы гарантируем, что if никогда не столкнется с ситуацией, когда ей нечего сравнивать, а нам это дает возможность обработать ситуацию, отловить, когда сравнение вдруг обезсмысливается.
for /f "usebackq tokens=1,2,3,4* skip=5" %a in (`"dir /-c | findstr /v /c:."`) do if %e.==. echo a=%a b=%b c=%c d=%d e=%e
Ну как вам ?
Коротко и просто.
Можно чуть украсить вывод:
for /f "usebackq tokens=1,2,3,4* skip=5" %a in (`"dir /-c | findstr /v /c:."`) do if %e.==. echo Total files is %a sized %c bytes
Если вы вырабатываете какой-нибудь отчет, то перенаправив вывод команды echo сможете получить симпатичный отчет об объеме всех файлов.
Блоки команд. Переменные окружения.>>>>>
(c) 2006-2012
by Foto-Workshop.
Перепечатка или цитирование свободно при
условии, указания ссылки на данный источник,
но только в случае, если не оговорены дополнительные условия.