Как получить номер цикличного архива 1...N по дате в CMD?

Системный администратор IT и автоматизация бизнеса
← На главную

Нам часто требуется автоматизировать процесс создания резервных копий или архивов таким образом, чтобы они циклически перезаписывались, сохраняя определенное количество последних версий. Например, мы хотим иметь N архивов (с номерами от 1 до N), которые будут сменяться по дням, где номер архива определяется по текущей дате. Это позволяет избежать бесконечного накопления файлов и упрощает управление бэкапами.

Основная идея заключается в использовании математической операции деления по модулю. Если мы возьмем количество дней, прошедших с некоторой фиксированной даты (например, 1 января 2000 года), и разделим это число на желаемое количество цикличных архивов N, то остаток от деления даст нам номер архива (скорректированный для диапазона 1...N).

Задача формулируется следующим образом: как в среде командной строки Windows (CMD) реализовать алгоритм, который выдает номер архива от 1 до N, используя остаток от деления номера дня (считая от 1 января 2000 года или любой другой базовой даты) на N.

Концептуальное решение: Формула на 1С (для понимания логики)

Для начала, давайте рассмотрим, как эта логика может быть реализована в среде, где работа с датами и математическими операциями более развита, например, в 1С. Это поможет нам понять основной алгоритм, который затем мы будем адаптировать для CMD.

В 1С, формула для получения цикличного номера архива будет выглядеть следующим образом:


N = 5; // Желаемое количество цикличных архивов
R = ((НачалоДня(ТекущаяДата()) - '20010101') / 3600 / 24) % N + 1;
Сообщить(R);

Разберем эту формулу по шагам:

  1. НачалоДня(ТекущаяДата()): Мы получаем текущую дату без времени, что важно для точного подсчета дней.
  2. '20010101': Это базовая дата, от которой мы начинаем отсчет дней. Вы можете выбрать любую удобную дату, главное, чтобы она была константой для всех расчетов.
  3. (НачалоДня(ТекущаяДата()) - '20010101'): Эта операция в 1С возвращает разницу между двумя датами в секундах.
  4. / 3600 / 24: Мы делим разницу в секундах сначала на количество секунд в часе (3600), а затем на количество часов в сутках (24). Таким образом, мы получаем общее количество полных дней, прошедших с базовой даты до текущего дня.
  5. % N: Это оператор деления по модулю (остаток от деления). Он возвращает остаток от деления количества дней на N. Если N равно 5, остаток будет в диапазоне от 0 до 4.
  6. + 1: Мы добавляем единицу к результату, чтобы номер архива был в диапазоне от 1 до N (в нашем примере от 1 до 5), а не от 0 до N-1.

Именно эту логику нам нужно воспроизвести или сымитировать в командной строке Windows.

Решение через VBScript, вызываемый из CMD

Чистый CMD обладает ограниченными возможностями для работы с датами и выполнения сложных математических операций. Однако, мы можем использовать встроенные в Windows языки сценариев, такие как VBScript, для выполнения вычислений и получения результата, который затем будет использован в BAT-файле.

Шаг 1: Создаем VBScript файл

Создайте файл с именем get_archive_number.vbs (например, в папке C:\Scripts\) и поместите в него следующий код. Этот скрипт вычислит количество дней с заданной начальной даты, применит операцию деления по модулю и выведет полученный номер архива.


Set objDate1 = CreateObject("WbemScripting.SWbemDateTime")
objDate1.SetVarDate #01/01/2000# ' Начальная дата, от которой ведется отсчет
    
Set objDate2 = CreateObject("WbemScripting.SWbemDateTime")
objDate2.SetVarDate Now ' Текущая дата
    
' Вычисляем разницу в днях
days_diff = DateDiff("d", objDate1.GetVarDate, objDate2.GetVarDate)
    
N = 5 ' Желаемое количество цикличных архивов (например, 5)
    
' Вычисляем номер архива (от 1 до N)
archive_number = (days_diff Mod N) + 1
    
WScript.Echo archive_number

Давайте подробнее рассмотрим ключевые элементы этого VBScript:

Шаг 2: Вызываем VBScript из BAT-файла

Создайте BAT-файл (например, create_archive.bat) в той же папке или в любом другом месте. Этот файл будет вызывать VBScript, захватывать его вывод и использовать полученный номер для формирования имени архива.


@echo off
REM Указываем путь к вашему VBS-скрипту
set "VBS_SCRIPT=C:\Scripts\get_archive_number.vbs"
    
REM Вызываем VBScript и захватываем его вывод в переменную ARCHIVE_NUM
for /f "usebackq" %%a in (`cscript //nologo "%VBS_SCRIPT%"`) do set "ARCHIVE_NUM=%%a"
    
echo Номер архива на сегодня: %ARCHIVE_NUM%
    
REM Теперь вы можете использовать %ARCHIVE_NUM% в команде создания архива
REM Например, для создания архива с именем ARCHIVE_1.rar, ARCHIVE_2.rar и т.д.
REM Предположим, у вас установлен WinRAR, и rar.exe находится в системном PATH.
set "ARCHIVE_PATH=C:\Archives"
set "SOURCE_PATH=C:\SourceData"
set "ARCHIVE_NAME=backup"
    
echo Создаем архив %ARCHIVE_PATH%\%ARCHIVE_NAME%_%ARCHIVE_NUM%.rar
rar a -ep1 "%ARCHIVE_PATH%\%ARCHIVE_NAME%_%ARCHIVE_NUM%.rar" "%SOURCE_PATH%"
    
echo.
echo Архив создан.

Рассмотрим команды BAT-файла:

Решение через PowerShell, вызываемый из CMD

PowerShell является более современной и мощной оболочкой для автоматизации в Windows, доступной в большинстве современных систем. Он предлагает еще более удобные средства для работы с датами и математикой, а его использование аналогично VBScript.

Шаг 1: Создаем PowerShell скрипт

Создайте файл с именем get_archive_number.ps1 (например, в папке C:\Scripts\) и добавьте в него следующий код:


$startDate = [datetime]"2000-01-01" # Начальная дата
$currentDate = [datetime]::Now   # Текущая дата
    
$timeSpan = $currentDate - $startDate
$daysDiff = $timeSpan.Days       # Разница в днях
    
$N = 5 # Желаемое количество цикличных архивов
$archiveNumber = ($daysDiff % $N) + 1
    
Write-Host $archiveNumber

Давайте разберем этот PowerShell скрипт:

Шаг 2: Вызываем PowerShell скрипт из BAT-файла

В вашем BAT-файле (например, create_archive_ps.bat) вызовите PowerShell скрипт и захватите его вывод. Разместите этот BAT-файл в удобном для вас месте, например, там же, где и скрипт.


@echo off
REM Указываем путь к вашему PowerShell скрипту
set "PS_SCRIPT=C:\Scripts\get_archive_number.ps1"
    
REM Вызываем PowerShell скрипт и захватываем его вывод в переменную ARCHIVE_NUM
for /f "usebackq" %%a in (`powershell -ExecutionPolicy Bypass -File "%PS_SCRIPT%"`) do set "ARCHIVE_NUM=%%a"
    
echo Номер архива на сегодня: %ARCHIVE_NUM%
    
REM Далее используем %ARCHIVE_NUM% для создания архива
set "ARCHIVE_PATH=C:\Archives"
set "SOURCE_PATH=C:\SourceData"
set "ARCHIVE_NAME=backup"
    
echo Создаем архив %ARCHIVE_PATH%\%ARCHIVE_NAME%_%ARCHIVE_NUM%.rar
rar a -ep1 "%ARCHIVE_PATH%\%ARCHIVE_NAME%_%ARCHIVE_NUM%.rar" "%SOURCE_PATH%"
    
echo.
echo Архив создан.

Ключевые моменты при вызове PowerShell из BAT-файла:

Ограниченное решение на чистом CMD (на основе дня месяца)

Если вам не требуется строгая привязка к "дням от 1 января 2000 года", а достаточно цикличной нумерации в пределах месяца (т.е. номер архива будет циклично меняться каждый месяц, используя только день месяца), то можно использовать чистый CMD. Это решение не совсем соответствует изначальной постановке задачи, но может быть полезно для более простых сценариев, где не нужна точная дата-зависимая цикличность на протяжении многих лет, а достаточно ежемесячной.

Рассмотрим пример, где номер архива определяется на основе текущего дня месяца:


@echo off
    
REM Извлечение текущего дня месяца
REM Формат команды 'date /t' может отличаться в зависимости от локали Windows.
REM Пример для формата 'ДД.ММ.ГГГГ' или 'Пт 20.10.2006'
    
set "current_day_raw="
for /f "tokens=1,2,3,4 delims=. " %%a in ('date /t') do (
    REM Проверяем, если первый токен это день недели (например, "Пт"),
    REM то день месяца будет во втором токене.
    REM Иначе, если дата начинается сразу с дня (например, "20.10.2006"),
    REM то день месяца будет в первом токене.
        
    echo. | findstr /i /l "%%a" "Пн Вт Ср Чт Пт Сб Вс" >nul && (
        set "current_day_raw=%%b"
    ) || (
        set "current_day_raw=%%a"
    )
)
    
REM Убираем ведущий ноль, если день месяца меньше 10 (например, 05 -> 5)
REM Это необходимо, так как set /A интерпретирует числа с ведущим нулем как восьмеричные.
set /A current_day = 100%current_day_raw% %% 100
    
set /A N=5
set /A ARCHIVE_NUM=(%current_day% %% N) + 1
    
echo Номер архива на основе дня месяца: %ARCHIVE_NUM%
    
REM Пример использования для создания архива:
set "ARCHIVE_PATH=C:\Archives"
set "SOURCE_PATH=C:\SourceData"
set "ARCHIVE_NAME=monthly_backup"
    
echo Создаем архив %ARCHIVE_PATH%\%ARCHIVE_NAME%_%ARCHIVE_NUM%.rar
rar a -ep1 "%ARCHIVE_PATH%\%ARCHIVE_NAME%_%ARCHIVE_NUM%.rar" "%SOURCE_PATH%"
    
echo.
echo Архив создан.

В этом примере мы выполняем следующие действия:

Важно помнить, что это решение будет давать цикличный номер архива в пределах месяца. Например, если N=5, то 1-го числа месяца будет архив №2, 2-го – №3, 3-го – №4, 4-го – №5, 5-го – №1, 6-го – №2 и так далее (поскольку (1%5)+1 = 2, (2%5)+1 = 3 и т.д., (5%5)+1 = 1). Это отличается от исходной задачи, где цикличность должна быть по дням с начала эпохи, охватывая многие годы.

Заключение

Мы рассмотрели различные подходы к получению цикличного номера архива по дате в среде Windows. Хотя чистый CMD имеет ограничения для сложных операций с датами, использование вспомогательных скриптов на VBScript или PowerShell позволяет легко реализовать требуемую логику деления по модулю. Выбор конкретного метода зависит от ваших предпочтений и требований к совместимости с версиями Windows.

Для универсального и точного решения, соответствующего алгоритму (количество_дней_с_базовой_даты % N) + 1, рекомендуем использовать PowerShell или VBScript, которые могут быть бесшовно интегрированы в ваши BAT-файлы. Эти методы обеспечивают надежность и точность расчетов дат.

Использование только дня месяца в чистом CMD может быть простым решением для некоторых сценариев, но оно не полностью соответствует исходной задаче о подсчете дней от фиксированной даты и обеспечивает цикличность только в пределах одного месяца. Будьте внимательны к этому ограничению при выборе данного метода.

← На главную