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

    1. Избавьтесь от пробелов по обе стороны от знака = в команде set "varname=varvalue" .
    2. Избегайте именования переменных с начальным номером . Чтение аргументов командной строки (параметров):

    Хотя 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

    Зарегистрируйтесь, используя адрес электронной почты и пароль

    Опубликовать как гость

    Электронная почта

    Требуется, но не отображается

    Опубликовать как гость

    Электронная почта

    Требуется, но не отображается

    Нажимая «Опубликовать свой ответ», вы соглашаетесь с нашими условиями обслуживания и подтверждаете, что прочитали и поняли нашу политику конфиденциальности и кодекс поведения.