Bat set: set | Microsoft Learn
Содержание
Засекаем время в bat/cmd
Вт 29 Март 2016
При всех моих стараниях уйти от Windows везде и навсегда не получается. Есть компьютер на работе, компьютеры друзей, собственный нетбук, с которым неохота возиться настолько кардинально. При этом, ряд задач в принципе выполняется из командной строки проще и удобнее, чем «оконно-мышевым» способом. Особенно если это задачи из тех, что имеют прямое отношение к Linux, например, работа с утилитами из комплекта gnuwin32.
Запуская однажды уже упомянутую упаковку/распаковку при помощи windows-реализации tar, я захотел однажды засечь время. Ну, не секундомером же это делать… Сделал командный файл, в котором вывожу время, запускаю архиватор, снова вывожу время.
@time \t @tar -cf foo.tar foo @time \t
Получается ерунда. Команда time \t
выдает время с точностью до минуты. Ладно, можно использовать переменную среды %time%
:
@echo %time% @tar -cf foo. tar foo @echo %time%
Чуть получше, но все равно очень плохо. Во-первых, если архиватору добавить ключ -v, и он начнет выдавать в консоль имена архивируемых объектов, то время начала стремительно унесется за пределы экрана. Во-вторых, чтобы понять, сколько процесс занял времени, все равно придется считать, а компьютер на что?
Однако переменная %time%
хитроформатированная, скорее текст, чем число, и ее в лоб не получится использовать для вычитания времени начала из времени окончания. Тем более, командный интерпретатор cmd.exe работает с целочисленными значениями, а время идет с точностью до сотых секунды (ну, или до десятков миллисекунд, как хотите).
Ладно, разберем время на составляющие. При помощи конструкции set t1_m=%t1:~3,2%
которая присваивает переменной t1_m два знака из t1, пропустив первые три, получим часы, минуты, секунды и сотые доли секунды:
set t1_h=%t1:~0,2% set t1_m=%t1:~3,2% set t1_s=%t1:~6,2% set t1_ms=%t1:~9% set /a s1= t1_h * 60 * 60 * 100 + t1_m * 60 * 100 + t1_s * 100 + t1_ms
Напоминаю, что ключик /a
позволяет оператору set
не только присваивать значение переменной, но и выполнять некоторые операции (подробнее — set /?
).
В результате получили в s1 время в сотнях миллисекунд, допустим, из времени начала операции. Можно сделать аналогичным образом s2 из времени окончания. Эти две переменные целочисленные и прекрасно складываются, вычитаются и все такое. Нам надо разность, и, чтобы избежать неожиданностей со знаками, можно сразу определить, что из них больше, и потом вычитать:
if /I %s1% geq %s2% (set /a s3= s1 - s2) else (set /a s3= s2 - s1)
Ключик /i
расширяет возможности оператора сравнения, позволяя ему не только выполнять сравнение строк на тождественность (чем он занимался изначально), но и сравнивать числа на больше/меньше/равно (if /?
)
Получили s3 — разность времени в десятках миллисекунд. Надо разобрать на составляющие, а потом склеить обратно в удобочитаемом виде. Все несложно, используем остаток от деления, чтобы избавиться, например, от часов при извлечении минут, и деление (целочисленное, другого тут нет), чтобы избавиться от секунд, примерно так:
@set /a t3_h= s3 / 100 / 60 / 60 @set /a t3_m= s3 %% (100 * 60 * 60) / 100 / 60 @set /a t3_s= s3 %% (100 * 60) / 100 @set /a t3_ms= s3 %% 100
Тут надо не забыть забавную тонкость: в командном интерпретаторе символ процента — подводный камень. Иногда он ставится по обе стороны от переменной, и тогда при выполнении интерпретатор отбрасывает эту пару, а имя переменной заменяет значением. Иногда он стоит перед переменной цикла. Иногда он используется в виде оператора получения остатка от целочисленного деления. Так вот, там, где процент непарный, его надо не забывать удваивать, а то его отбросят и будут безуспешно искать пару.
Получили часы, минуты, секунды и десятки миллисекунд в отдельных переменных. Можно их использовать, как числа, а можно — как строки, поэтому их легко склеить в одно значение времени и выдать его в стандартный поток ввода-вывода, «на консоль»:
@set t3=%t3_h%:%t3_m%:%t3_s%,%t3_ms% @echo %t3%
Все это можно засунуть в отдельный файл, чтобы использовать в разных местах. Тогда время надо будет передавать файлу в виде параметров. Тут есть два варианта: либо
@set t1=%~1 @set t2=%~2
и передавать параметры в двойных кавычках (а тильда раскроет кавычки), либо
@set t1=%1%2 @set t2=%3%4
и передавать без кавычек, потому что в символьном представлении времени , по крайней мере, с русской локалью (в другой не проверял), десятки миллисекунд отделяются от секунд запятой, а запятая вместе с пробелом разделяет и параметры командной строки, так что сотые доли секунды будут уже другим параметром.
Итого, получился такой командный файл, скажем, timecalc.bat:
@set t1=%~1 @set t2=%~2 @rem параметры должны быть в кавычках @rem echo t1 = %t1% @rem set t1=03:02:20,45 @set t1_h=%t1:~0,2% @set t1_m=%t1:~3,2% @set t1_s=%t1:~6,2% @set t1_ms=%t1:~9% @rem echo %t1% = %t1_h% - %t1_m% - %t1_s% - %t1_ms% @set /a s1= t1_h * 60 * 60 * 100 + t1_m * 60 * 100 + t1_s * 100 + t1_ms @rem echo s1 = %s1% @rem set t2=01:01:10,05 @rem echo t2 = %t2% @set t2_h=%t2:~0,2% @set t2_m=%t2:~3,2% @set t2_s=%t2:~6,2% @set t2_ms=%t2:~9% @rem echo %t2% = %t2_h% - %t2_m% - %t2_s% - %t2_ms% @set /a s2= t2_h * 60 * 60 * 100 + t2_m * 60 * 100 + t2_s * 100 + t2_ms @rem echo s2 = %s2% @if /I %s1% geq %s2% (set /a s3= s1 - s2) else (set /a s3= s2 - s1) @rem set /a s3= s2 - s1 @rem echo s3 = %s3% @set /a t3_h= s3 / 100 / 60 / 60 @set /a t3_m= s3 %% (100 * 60 * 60) / 100 / 60 @set /a t3_s= s3 %% (100 * 60) / 100 @set /a t3_ms= s3 %% 100 @rem echo = %t3_h% - %t3_m% - %t3_s% - %t3_ms% @set t3=%t3_h%:%t3_m%:%t3_s%,%t3_ms% @echo %t3%
Теперь об использовании. Допустим, есть такой батничек, запускающий архивацию с засечкой времени:
@set a=%time% @echo start %a% @tar -cf %1.tar %1 @set b=%time% @echo stop %b%
В переменных a и b имеем время начала и окончания операции соответственно. Можно передать их нашему скрипту, непременно валяющемуся тут же рядом, в этой же папке:
call .\timecalc.cmd "%b%" "%a%"
И он выведет в консоль затраченное время третьей строчкой, после «start <время начала>» и «stop <время окончания>». Но хотелось бы добавить, допустим, «упаковано за » или «распаковано за », так что ввести соответствующие строки в файл «подпрограммы» не получится. Передавать этот строковый «довесок» в виде еще одного параметра и не особо красиво, и при желании дописать текст после времени (а не перед) опять потребует лезть в вызываемый файл. А как перехватить вывод вызываемого файла для использования в вызывающем?
Я просто обалдел, когда узнал. Внезапно, при помощи оператора цикла!!! О, сколько нам открытий чудных готовит for /?
…
Расширение синтаксиса /F
позволяет выковыривать переменную цикла из разного рода наборов: файлов, символьных строк, команд. Чтобы понять, что используется для цикла, применяются (или не применяются) кавычки. Без кавычек в скобках пишем файл или набор файлов. А применение кавычек зависит от того, установлена или нет опция usebackq: при установленной в двойных кавычках пишем строку, в простых — команду. При снятой опции строку пишем в простых кавычках, а команду — в обратных (которые обычно на клавише с буквой Ё). При этом, команда вполне может быть одна, и проход цикла тоже один, и в теле цикла получаем нужный нам вывод команды, который засунем в переменную для дальнейшего использования (хотя можно и использовать сразу):
@for /F "usebackq" %%s in (`call .\timecalc.cmd "%b%" "%a%"`) do @set delta=%%s
И, кстати, не забываем, что переменная цикла должна быть односимвольной. Нигде этого не прочитал, вывел экспериментально путем ошибок и проб. Может, читал невнимательно, а может, не знал где…
Итого получаем такой командный файл, скажем, tar_time.cmd, который принимает в качестве параметра пакуемую папку, засекает время и пишет его в консоль и в лог-файл:
@echo %1 >> tar_log.txt @set a=%time% @echo start %a% @echo start %a% >> tar_log.txt @tar -cf %1.tar %1 @set b=%time% @echo stop %b% >> tar_log.txt @echo stop %b% @for /F "usebackq" %%s in (`call .\timecalc.cmd "%b%" "%a%"`) do @set delta=%%s @echo %delta% >> tar_log.txt @echo packed for %delta%
Аналогичным образом написал почти то же для распаковки, поправив пару строчек.
Конечно, можно кое-что оптимизировать, например, все вычисления с константами сделать на калькуляторе (или в уме), преобразование времени в сотые доли секунды вынести в «функцию» и вызывать ее через call
, но так удобочитаемее, а на скорость выполнения влияет мало. Еще можно вместо своры собак (@
) поставить один раз в начале файла @echo off
, дело вкуса. И, конечно, можно повнимательнее относиться к регистру символов и писать либо везде прописными, либо везде строчными. Но, как уж получилось…
Возможности команд Windows (тяжелое наследие DOS), конечно, довольно ограничены, но даже с их помощью можно делать интересные и полезные штучки. Вот только иногда фиг догадаешься, описание какой команды для твоей цели нужно смотреть и где именно искать…
пакетов Windows SET внутри, если не работает
спросил
Изменено
12 месяцев назад
Просмотрено
95 тысяч раз
, когда я запускаю этот скрипт (из файла . bat):
set var1=true если "%var1%"=="истина" ( установить var2 = мое значение эхо %var2% )
Я всегда получаю:
ЭХО включено.
Значение переменной var2
на самом деле не установлено.
Может ли кто-нибудь помочь мне понять, почему?
- Windows
- Пакетный файл
- CMD
- Задержка.
В настоящее времяvar2
пуст.Следовательно, синтаксис delayedExpansion существует, он использует
!
вместо%
, и он оценивается во время выполнения, а не во время синтаксического анализа.Обратите внимание, что для использования
!
требуется дополнительная инструкцияsetlocal EnableDelayedExpansion
.setlocal EnableDelayedExpansion установить var1 = истина если "%var1%"=="истина" ( установить var2 = мое значение эхо !var2! )
3
Я немного опоздал на вечеринку, но еще один способ справиться с этим условием — продолжить процесс за пределами
если
, вот такустановить var1=true если "%var1%"=="истина" ( установить var2 = мое значение ) эхо %var2%
Или/и используйте
goto
синтаксисset var1=true если "%var1%"=="истина" ( установить var2 = мое значение перейти на линию 10 ) еще ( перейти на линию 20 ) . . . . . :строка10 эхо %var2% . . . . . :line20
Таким образом расширение происходит «вовремя» и вам не нужно
setlocal EnableDelayedExpansion
. В итоге, если вы переосмыслите дизайн своего скрипта, вы можете сделать это так2
Зарегистрируйтесь или войдите в систему
Зарегистрируйтесь с помощью Google
Зарегистрироваться через Facebook
Зарегистрируйтесь, используя адрес электронной почты и пароль
Опубликовать как гость
Электронная почта
Требуется, но никогда не отображается
Опубликовать как гость
Электронная почта
Требуется, но не отображается
Нажимая «Опубликовать свой ответ», вы соглашаетесь с нашими условиями обслуживания и подтверждаете, что прочитали и поняли нашу политику конфиденциальности и кодекс поведения.
окон — переменная SET не читается в файле .BAT
спросил
Изменено
6 лет, 5 месяцев назадПросмотрено
886 разУ меня есть пакетный файл Windows, и я не могу заставить его работать должным образом.
По сути, у меня есть папка в одном месте, и мне нужно скопировать ее в другую папку, но также переименовать новое имя папки.@Эхо выключено :: установить 836147398 = @Taunus_A3 :: Эхо-копирование %836147398% xcopy "C:\Users\arrona\Desktop\Update Mods\steamapps\workshop\content\107410\836147398" "C:\TCAFiles\Games\arma3exile\TCA.Mods\%836147398%" /s/h/e/k / ф / с
Так что мне нужно, чтобы содержимое папки 333310405 было скопировано в
C:\TCAFiles\Games\arma3exile\TCA. Mods\
, но как@Taunus_A3
имя папки.Будет скопирована папка и все содержимое, но переменная не будет считана. Это будет эхо «Копирование 836147398», а не «Копирование @Taunus_A3»
- windows
- пакетный файл
- set
2
- Избавьтесь от пробелов по обе стороны от знака
=
в командеset "varname=varvalue"
. - Избегайте именования переменных с начальным номером . Чтение аргументов командной строки (параметров):
Хотя
set "836147398=@Taunus_A3"
является допустимой командой,echo %836147398%
отобразит-
@Taunus_A3
в командной строке, но 90 024 -
36147398
в пакетном скрипте, потому что%8
оценивается по 8-му предоставленному параметру (предположительно, в большинстве случаев это пустая строка), а знак сиротского конца%
процента игнорируется по замыслу.
Решение : используйте префикс без шифрования, например.
_
подчеркивание следующим образом:установить "_836147398=@Taunus_A3" эхо %_836147398%
5
Избавьтесь от пробелов по обе стороны от знака
=
. Как вы можете видеть из второгоecho
, вы фактически создаете переменную среды с встроенным пробелом в имени (и значении, если уж на то пошло):c:\pax> set 42= c:\pax> установить 42 = 57 c:\pax> эхо .%42%. .%42%. c:\pax> эхо .%42 %. . 57. c:\pax> установить 42=57 c:\pax> эхо .%42%. .57.
Как только это сделано, подстановки работают как надо, по крайней мере, в командной строке (a) (хотя ваша исходная область отличается кодом и текстом, вы, вероятно, захотите разобраться и с этим):
c :\pax> установить 836147398=@Taunus_A3 c:\pax> echo xcopy "C:\Users\arrona\Desktop\Update Mods\steamapps\workshop\content\107410\836147398" "C:\TCAFiles\Games\arma3exile\TCA. Mods\%836147398%" /s / ч / д / к / ф / к xcopy "C:\Users\arrona\Desktop\Update Mods\steamapps\workshop\content\107410\836147398" "C:\TCAFiles\Games\arma3exile\TCA.Mods\@Taunus_A3" /s/h/e/k/f/c
(a) Однако следует иметь в виду, что
836147398
на самом деле является особенно плохим именем переменной, отчасти потому, что в скрипте фактически используются имена параметров (например,% 8
) вместо%8-something-or-other%
, но также и потому, что это не очень мнемонично.Вам было бы далеко лучше использовать что-то вроде
dest_dir
, что-то, что действительно имеет смысл для случайного читателя.7
Зарегистрируйтесь или войдите в систему
Зарегистрируйтесь с помощью Google
Зарегистрироваться через Facebook
Зарегистрируйтесь, используя адрес электронной почты и пароль
Опубликовать как гость
Электронная почта
Требуется, но не отображается
Опубликовать как гость
Электронная почта
Требуется, но не отображается
Нажимая «Опубликовать свой ответ», вы соглашаетесь с нашими условиями обслуживания и подтверждаете, что прочитали и поняли нашу политику конфиденциальности и кодекс поведения.