Как скопировать файл с увеличенным номером версии в имени файла в зависимости от существующих файлов? - PullRequest
0 голосов
/ 07 мая 2020

У меня есть командный файл ниже:

FOR /F "delims=|" %%I IN ('DIR "%C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads\*.xlsx" /B /O:D') DO SET NewestFile=%%I

FOR /F "delims=" %%a IN ('wmic OS Get localdatetime ^| find "."') DO SET DateTime=%%a

set Yr=%DateTime:~0,4%
set Mon=%DateTime:~4,2%
set Day=%DateTime:~6,2%

setlocal enableDelayedExpansion
set "baseName=InventoryReport%Yr%-%Mon%-%Day% V1.%n%"
set "n=0"
FOR /f "delims=" %%F in (
  'DIR /b /ad "%baseName%*"^|findstr /xri "\\192.168.0.141\Medisun\28 - Business Development\30 - Product Inventory\InventoryReport\"%baseName%[0-9]*""'
) do (
  set "name=%%F"
  set "name=!name:*%baseName%=!"
  if !name! gtr !n! set "n=!name!"
)
set /a n+=1
md "%baseName%%n%"


copy "%C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads\%NewestFile%" "\\192.168.0.141\Medisun\28 - Business Development\30 - Product Inventory\InventoryReport\%baseName%%n%.xlsx"
cmd /k

Я не могу найти самый большой номер версии ранее скопированного файла между V1. и расширением файла .xlsx в имени файла и увеличить его, но один. Пакетный файл находит файл V1.1, но перезаписывает его вместо копирования самого нового файла с V1.2 в имени целевого файла.

Как я могу сначала получить предыдущую версию файла и увеличить это число?

1 Ответ

1 голос
/ 08 мая 2020

Задача копирования файла может быть выполнена с помощью следующего командного файла:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads"
set "TargetFolder=\\192.168.0.141\Medisun\28 - Business Development\30 - Product Inventory\InventoryReport"

for /F "eol=| delims=" %%I in ('dir "%SourceFolder%\*.xlsx" /A-D /B /O-D 2^>nul') do set "NewestFile=%%I" & goto CheckTarget
echo ERROR: Found no *.xlsx file in the folder:
echo        "%SourceFolder%"
exit /B 1

:CheckTarget
if not exist "%TargetFolder%\" md "%TargetFolder%\" 2>nul
if exist "%TargetFolder%\" goto GetDateTime
echo ERROR: Failed to access or create the folder:
echo        "%TargetFolder%"
exit /B 2

:GetDateTime
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "DateTime=%%I"
set "BaseName=InventoryReport%DateTime:~0,4%-%DateTime:~4,2%-%DateTime:~6,2% V1"

set "FileNumber=-1"
setlocal EnableDelayedExpansion
for /F "tokens=2 delims=." %%I in ('dir "!TargetFolder!\!BaseName!.*.xlsx" /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /X /C:"!BaseName!\.[0123456789][0123456789]*\.xlsx"') do if %%I GTR !FileNumber! set "FileNumber=%%I"
endlocal & set "FileNumber=%FileNumber%"

set /A FileNumber+=1
copy /B /V "%SourceFolder%\%NewestFile%" "%TargetFolder%\%BaseName%.%FileNumber%.xlsx" >nul ||  exit /B 3
endlocal

Первый FOR l oop запускает в фоновом режиме еще один командный процесс с %ComSpec% /c и командная строка в круглых скобках в качестве дополнительных аргументов. Таким образом выполняется с Windows, установленным в C:\Windows в фоновом режиме:

C:\Windows\System32\cmd.exe /c dir "C:\TeamCity\buildAgent\work\53bba593f5d69be\public\uploads\*.xlsx" /A-D /B /O-D 2>nul

Фоновый командный процесс выполняет внутреннюю команду DIR , которая

  • ищет в указанный каталог
  • только для имен файлов из-за опции /A-D (атрибут, а не каталог)
  • соответствует шаблону подстановки *.xlsx и
  • выводит их в чистом формате с просто имя файла + расширение из-за опции /B
  • упорядочено в обратном порядке по дате последнего изменения из-за опции /O-D, что означает, что имя самого нового файла выводится первым, а имя самого старого файла выводится last.

Возможно, что либо исходный каталог не существует вообще, либо исходный каталог не содержит ни одного файла, соответствующего этим критериям. DIR будет выводить в этом случае сообщение об ошибке для обработки STDERR фонового командного процесса, который будет перенаправлен командным процессом, обрабатывающим пакетный файл, на собственный дескриптор STDERR и поэтому отображается, скорее всего, в окне консоли. Это сообщение об ошибке нежелательно, так как под FOR l oop есть лучший результат, если не найдено ни одного файла для копирования. По этой причине сообщение об ошибке перенаправляется уже фоновым командным процессом на устройство NUL для его подавления.

Прочтите документацию Microsoft о Использование операторов перенаправления команд для объяснения из 2>nul. Оператор перенаправления > должен быть экранирован символом вставки ^ в командной строке FOR , чтобы интерпретироваться как буквальный символ, когда интерпретатор команд Windows обрабатывает эту командную строку перед выполнением команды FOR который выполняет встроенную dir командную строку в отдельном командном процессе, запущенном в фоновом режиме.

FOR захватывает все, что написано для обработки STDOUT фонового командного процесса и процессов этот захваченный вывод построчно после того, как выполненный фон cmd.exe завершился сам.

FOR с опцией /F всегда игнорирует пустые строки, которых нет в этом случае. Каждая вторая строка сначала будет разделена на подстроки с использованием обычного пробела и символа горизонтальной табуляции в качестве разделителей. Строка будет проигнорирована, если первая строка, разделенная пробелом / табуляцией, начинается с символа конца строки по умолчанию ; (точка с запятой). В противном случае только первая строка, разделенная пробелом / табуляцией, будет назначена l oop переменной I, и команда, соответственно, командный блок будет выполняться следующим.

Имя файла * .xlsx может содержать один или несколько пробелов . По этой причине параметр FOR delims= используется для определения пустого списка разделителей строк, чтобы полностью отключить разделение строк. Это необычно, но, тем не менее, возможно, чтобы имя файла начиналось с точки с запятой. Поэтому FOR опция eol=| также используется для определения вертикальной полосы как символа конца строки, который не может содержать имя файла, как описано Microsoft в документации о именовании файлов, путей и пространств имен . Таким образом, в результате каждое имя файла, выводимое DIR в фоновом командном процессе, одно за другим полностью присваивается переменной l oop I.

Имя файла Самый новый файл выводится первым, поэтому его имя присваивается переменной среды NewestFile. Затем первый FOR l oop завершается с помощью команды GOTO для перехода к первой строке под меткой CheckTarget, поскольку обработка других имен файлов была бы пустой тратой времени и Мощность процессора.

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

Затем, имея имя файла самого нового файла в исходной папке, проверка существования целевой папки выполняется с созданием целевой папки, если она еще не существует. Значимое сообщение об ошибке выводится, если целевая папка все еще не существует из-за того, что другой компьютер или запоминающее устройство не работает или вообще недоступно, или создание целевой папки не удалось по какой-либо причине.

Следующее две командные строки получают текущую дату / время в независимом от региона формате и определяют базовое имя файла для целевого файла с использованием текущей даты. Полное описание этих двух строк см. В моем ответе на Время установлено неправильно после полуночи .

Тогда номер файла определяется значением -1 и отложенное раскрытие включен по мере необходимости для сравнения чисел, выполняемого следующим FOR l oop.

Третий FOR l oop аналогичен первому ДЛЯ l oop. Кроме того, вывод команды DIR перенаправлен на обработчик STDIN of FINDSTR , который будет отфильтрован для проверки, если имя найденного файла действительно содержит только один или несколько цифры между точкой после V1 и точкой расширения файла, т.е. эта часть имени файла является допустимым числом. Можно предположить, что FINDSTR выводит те же строки, что и вывод DIR в целевой папке, не используемой для чего-то другого, кроме файлов Excel с определенным шаблоном для имени файла. Две точки в имени каждого файла должны быть экранированы с помощью backsla sh в строке поиска интерпретируемого регулярного выражения без учета регистра, в которой пробел интерпретируется как буквальный символ из-за использования /C: и /R, а не как OR выражение как при опускании /C:. Для 100% безопасности при последующей обработке используются только правильные имена файлов /X, чтобы выводить только те имена файлов, в которых полное имя файла совпадает с поисковым выражением.

На этот раз FOR l oop не должно назначать полное имя файла переменной l oop I. Интересна только строка между первой точкой после V1 и расширением файла .xlsx. По этой причине параметр FOR delims=. используется для разделения имен файлов на точки, а параметр tokens=2 используется для указания команде FOR присвоить второй строке, разделенной точками, l oop переменная I, которая представляет собой увеличенный номер файла.

Простое целочисленное сравнение выполняется, чтобы определить, больше ли номер файла текущего имени файла, чем номер файла, присвоенный в настоящее время переменной среды FileNumber в в этом случае этот больший номер файла присваивается переменной среды FileNumber.

Локальная среда с включенным отложенным расширением больше не нужна после того, как известно наибольшее количество файлов из существующих файлов, если он вообще есть. Таким образом, эта среда уничтожается, что будет означать, что переменная среды FileNumber снова будет иметь номер -1, присвоенный переменной среды в исходной среде. Пожалуйста, прочтите этот ответ для получения подробной информации о командах SETLOCAL и ENDLOCAL . Таким образом, чтобы передать текущее значение FileNumber в текущей среде в FileNumber в предыдущей среде, командная строка с endlocal дополнительно содержит команду set "FileNumber=%FileNumber%", которая обрабатывается cmd.exe, например, set "FileNumber=12" перед выполнение команды ENDLOCAL . Этот простой трюк используется для передачи максимального значения номера файла в FileNumber в предыдущей среде.

См. Также:

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

Пакетный файл закрывается с кодом выхода 3 в случае сбоя копирования файла по какой-либо причине.

Наконец, обработка пакетного файла заканчивается явным восстановлением исходной среды выполнения . Последняя команда ENDLOCAL на самом деле не нужна, потому что Windows командный процессор запускает ее неявно при завершении обработки этого командного файла, как, например, при выполнении одной из трех команд exit /B.

Чтобы понять используемые команды и то, как они работают, откройте окно командной строки , выполните в нем следующие команды и внимательно прочтите все страницы справки, отображаемые для каждой команды.

  • copy /?
  • dir /?
  • echo /?
  • endlocal /?
  • exit /?
  • findstr /?
  • for /?
  • goto /?
  • set /?
  • setlocal /?
  • wmic /?
  • wmic os /?
  • wmic os get /?
  • wmic os get localdatetime /?

PS: максимально возможный номер файла - 2147483647. Но в день всего 86400 секунд, и более 65535 файлов в одном каталоге тоже могут стать настоящей проблемой. Таким образом, максимальное количество файлов 2147483647 никогда не должно быть достигнуто, если ни один пользователь не переименовывает файл в целевой папке, чтобы превышать это максимальное количество.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...