Блоки команд. Переменные окружения.

Теперь перейдем к более сложным конструкциям и решим ту же задачу, но иначе.
У shell есть одно ограничение по умолчанию, нельзя в блоке команд оперативно работать с переменными окружения.
Например:
for /f "usebackq tokens=1,2,3,4* skip=5" %a in (`dir /-c`) do set /a Test_cnt=%Test_cnt%,"%d"

Вроде бы должна создаваться строка переменной окружения "Test_cnt", содержащей оковыченными все имена файлов, но это не так.
Нельзя использовать их как переменные для временного хранения.
Так как дальше мы будем работать с блочными командами, то рекомендую вооружиться текстовым редактором, для редактирования и создения файлов, либо для создания использовать "дедовский способ".
copy con имя_файла.cmd

после этого вводите необходимые команды и заканчиваете создание файла, комбинацией Ctrl+Z, что означает конец файла, жмете Enter, и у вас создан файл. Но редактировать его все равно придется редактором, подойдет что угодно, хоть notepad.
В командной строке ему указываете какой файл он должен вам открыть для редактирования.
Итак: Проблемы с переменными.
Пример:
Подсчитаем количество файлов размером больше 20 килобайт.
Делаем простой и знакомый цикл:
Создаем файл, например Ex1.cmd

Работать не будет. Как не старайся.
Происходит это потому, что интерпретатор разворачивает исполняемую часть команды for, преобразовывая и считывая значения переменных окружения и подставляя их в команды.
Таким образом переменная "Test_cnt" всегда будет равняться "0" в команде сложения "set /a Test_cnt=%Test_cnt%+1".
На этом можно было бы поставить точку в shell программировании, если бы не дополнительный режим радикально изменяющий поведение инетпретатора в таких случаях.

Режим отложенного расширения переменных среды.
Активируется запуском CMD.EXE с ключем "/v:on".
По умолчанию он выключен. Что весьма разумно, ибо вы никогда не сможете обработать папку если она будет содержать "!" в своем названии. Эта ошибка, о которой лучше не забывать. Все операции с выводом на экран у вас будут работать, как запланировано, но дисковые операции будут спотыкаться.
Попробуйте:
md !TestDir
cd !TestDir
copy con Ex2.cmd
echo "%~dp0"
dir "%~dp0"
<Ctrl>+<Z>

Первая команда должна вывести диск и папку в которой находится ваш файл Ex2.cmd, в вторая должна вывести список файлов папки в которой находится Ex2.cmd. (Детально с возможными параметрами после тильды, можно ознакомиться запустив for /?.)
Запустите этот файл с выключенным и включенным режимом:
cmd /v:off ex2.cmd
cmd /v:on ex2.cmd

Первая команда отрабатывает всегда, а вторая выдаст ошибку во втором случае. Детальное рассмотрение процессов, показывает "утерю" восклицательного знака, то есть !TestDir становится TestDir, поэтому в лучшем случае выдается ошибка, а в худшем показывается содержимое другой папки.

На этом я закончу о плохом и продолжу о хорошем. Итак, если нам все таки надо подсчитать, нам дали возможность это сделать выделив нужную нам переменную восклицательными знаками. Поэтому перепишем предыдущий пример, с учетом этого нюанса: Открываем Ex1.cmd и корректируем:

Запускаем, смотрим.
Так то лучше.
Теперь мы и общий размер можем подсчитать, не забывая о том, что общий размер файлов должен быть меньше 2 гигабайт, еденичку зменяем на %%c и вот у нас уже считается общий размер файлов.

Теперь мы можем вернуться к нашей задаче о подсчете всех файлов и выводе их суммарного объема.
for /f "usebackq tokens=1,2,3,4* skip=5" %a in (`"dir /-c | findstr /v /c:."`) do ....

Так как мы намеряны делать все в ручную, смело избавляемся от фильтра findstr /v /c:. и получаем
for /f "usebackq tokens=1,2,3,4* skip=5" %a in (`"dir /-c"`) do....

Давайте создадим файл "Ex3.cmd" и начнем над ним эксперименты.
Для начала впишем туда следующую строку:
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 не выводить папки, а сосредоточиться на выводе только файлов.
Для этого вызовем ее с параметрами dir /-c /a-d, поэтому переписываем:
for /f "usebackq tokens=1,2,3,4* skip=5" %%a in (`dir /-c /a-d`) do echo a=%%a b=%%b c=%%c d=%%d e=%%e

Чтобы подсчитать количество и общий размер файлов, нам потребуется 2 переменные в которые мы будем складывать количество и размер.
Пусть это будет Temp_Cnt и Temp_Size.
В начале их лучше всего обнулить, чтобы не получалось так, что значения от предыдущего запуска мешали нам сейчас.
set Temp_Cnt=0
set Temp_Size=0

Теперь нам надо избавиться от последних двух строк, которые выдает команда dir, подсчитывая за нас суммарные объемы и количество свободного места. Мы можем это сделать вновь воспользовавшись фильтром, но мы этого делать не будем, а сделаем иначе.
Давайте еще раз посмотрим, чем выделяются эти строки ?
Первый токен не содержит ".", а второй не содержит ":".
За что зацеписмся ? К точкам уже цеплялись, давайте к ":" прицепимся.
Наша задача выделить строки, которые содеражат во втором токене ":".
Для этого воспользуемся вновь командой forи попросим ее разобрать второй токен на части использовав в качестве разделителя символ ":", выглядеть это будет примерно так: for /f "usebackq tokens=1,2 delims=:"...

Замечание!Когда вы используете команду FOR в команде FOR, то общее количество переменных у вас по прежнему ограничено 26 буквами, то есть если вы в первой команде описываете tokens=1,2,3,4* с начальной переменной "%%a", то у вас используются переменные: %%a, %%b, %%c, %%d, %%e и когда вы в do используете вторую констркцию FOR, к примеру с теми-же параметрами tokens=1,2,3,4* с начальной переменной "%%a", то все токены от предыдущего FOR будут утеряны навсегда.
Чтобы этого не произошло, начальную переменную надо указывать с учетом нужности предыдущих токенов. Так как первый использует от %%a до %%е, то второй FOR должен начинать отсчитывать свои токены от %%f, следующей за "E" буквой латинского алфавита.

С учетом этого знания можем переписать конструкцию:
set Temp_Cnt=0
set Temp_Size=0
for /f "usebackq tokens=1,2,3,4* skip=5" %%a in (`dir /-c /a-d`) do for /f "tokens=1,2 delims=:" %%f in ("%%b") do if %%g.==. (
echo String skipped: "%%a %%b %%c %%d %%e"
) else (
  if %%c GEQ 20480 (
    set /a Temp_Cnt=!Temp_Cnt!+1
    set /a Temp_Size=!Temp_Size!+%%c
    )
    Rem End of "if %%c GEQ 20480"
)
Rem End of "else for if %%g.==."
echo Files=%Temp_Cnt%
echo Total size=%Temp_Size%
set Temp_Cnt=
set Temp_Size=

Чтобы весь этот ужас отладочной информации нам на экран не выдавался, мы попросим интерпретатор, не выводить это всё командой echo off, а чтобы и сама команда не выводилась, временно отключим вывод символом "@", написав следующим образом:
@echo off

Давайте еще раз быстренько пробежим по тому, что мы тут написали:
for /f "usebackq tokens=1,2,3,4* skip=5" %%a in (`dir /-c /a-d`) do - эта команда разбирает на параметры результат работы команды dir с ключами /-c /a-d, если забыли, что делают эти ключи марш читать dir /?.

После того, как интерпретатор, выполнит dir и создаст у себя в памяти построчный список, этот список начнет построчно обрабатываться в соответствии с "usebackq tokens=1,2,3,4* skip=5" %%a и передаваться команде следующей за do, в виде проинициализированных токенов.

Вторая команда: for /f "tokens=1,2 delims=:" %%f in ("%%b") do - занимается дополнительным разбором токена %%b, который содержит время создания файла. Если разбор спотыкается, то есть знак ":" не попадается, то токен %%g будет пуст, что будет однозначно нам говорить о том, что в строке попалось нечто, что к информации о файле отношения не имеет.
И поэтому строками:
if %%g.==. (
echo String skipped: "%%a %%b %%c %%d %%e"
)

мы выводим сообщение о пропущенной строке, а так как параметры от "%%a" до "%%e" у нас описывают строку, то просто, выведя их последовательно, визуально мы реставрируем оригинальную строку, что для технического сообщения более, чем достаточно.
Дальше следует конструкция else.
Важно: else всегда должна следовать за скобкой в одной строке, также, как и открывающая скобка должна стоять на месте начала оператора. Следует помнить об еще одной ошибке: скобки обрабатываются практически в лоб, поэтому, если вы где-то в rem упомянули круглую скобку, то вся скобочная конструкция рассыпается, не забывайте об этом.

Aльтернативный ход.
Если if %%g.==. ( у нас описывал случай, когда разбор времени создания файла спотыкается, то здесь мы описываем противный этому случай, то есть когда разбор отрабатывает нормально, именно else и кодирует альтернативный ход условия if.
) else (
  if %%c GEQ 20480 (
    set /a Temp_Cnt=!Temp_Cnt!+1
    set /a Temp_Size=!Temp_Size!+%%c
    )
    Rem End of "if %%c GEQ 20480"
)
Rem End of "else for if %%g.==."

Дальше уже все понятно, похожая сравнительная конструкция "if %%c GEQ 20480 (" если размер файлов больше 20 килобайт(не забываем, что в килобайте 1024 байта и 20 килобайт - это 20480 байт, а не 20000), которая скобками обрамляет составной оператор, который должен исполниться в случае выполнения условия:
set /a Temp_Cnt=!Temp_Cnt!+1
set /a Temp_Size=!Temp_Size!+%%c
)

Для команды set ключ "/a" означает преход в режим калькулятора.
set /a Temp_Cnt=!Temp_Cnt!+1
а вот вторая часть представляет большой интерес. Обрамлением восклицательными знаками в команде !Temp_Cnt! мы адресуем переменную окружения Temp_Cnt, но явно указываем, интерпретатору, что он должен срочно и сейчас, достать то значение этой переменной, какое существует на данный момент времени, а не на момент, когда он начал выполнять самую первую родительскую команду, вложенной частью которой мы являемся. За дополнительную обработку восклицательных знаков и отвечает запуск cmd с ключем "/v:on". Команду set /a Temp_Cnt=!Temp_Cnt!+1 можно было бы переписать немного иначе: set /a Temp_Cnt+=1 заносим в Temp_Cnt результат сложения ее значения с еденицей, но в таком случае это будет эквивалентно: set /a Temp_Cnt=%Temp_Cnt%+1, что не совсем одно и тоже
Дальше совсем уже начинается скукота:
echo Files=%Temp_Cnt%
echo Total size=%Temp_Size%
set Temp_Cnt=
set Temp_Size=
Последние две строки удаляют из списка переменных окружения использованные нами переменные.

По большому счету это все.
Конечно, если бы я писал книгу, то есть в условиях, где платят за объем, а не произведенный эффект, то было-бы много скриншотов, я бы за вас и писал, и выполнял, и давал бы разъяснения и описывал бы массу ненужных деталей, ссылок.
Пробуйте, используйте утилиты ищите возможности.
В следующей части, я приведу некоторые примеры по автоматизации некоторых процессов.
А пока для затравки, если вы используете протоколирование и вам надо вывести какую либо строку, но с отметкой времени, пожалуйста:
Можно воспользоваться всегда готовыми переменными окружения %time% и %date%
echo Date=%date% Time=%Time%

Если вас смущает количество символов при выводе времени, его можно ограничить начальным и конечными символами
echo Date=%date% Time=%Time:~0,5% - c 0го до 5го вывести, остальное отбросить

Можно эту задачу решить и более тяжелым путем:
for /f "usebackq" %%y in (`date /t`) do for /f "usebackq" %%z in (`time /t`) do echo Date=%%y Time=%%z

Терям две переменных %%y и %%z, и заставляем интерпретатор отработать команду, перехватить вывод и отдать на разбор по правилам. Это долго.

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

Hosted by uCoz