Как сжать все файлы, за исключением самого нового файла в каждой подпапке папки, в один ZIP-файл для каждой подпапки?

Я пытаюсь создать пакетный скрипт, который заархивирует все содержимое в каждом подкаталоге, кроме самого последнего (или нескольких последних). В настоящее время я пытаюсь использовать 7-Zip в Windows, но технически каталог находится на сервере Linux, поэтому приветствуются любые предложения, связанные с командой Linux.

Структура каталогов такая:

Directory-Parent
 ---Sub-Directory-1
 --------File1
 --------File2
 --------File3
 ---Sub-Directory-2
 --------File1
 --------File2

Я хотел бы запустить пакет на уровне Directory-Parent, который создаст zip в каждом подкаталоге всех файлов, кроме последнего 1 (или нескольких, если возможно). Я также хочу добавить год в конец имени zip-файла.

Таким образом, результатом будет:

Directory-Parent
 -Sub-Directory-1
 --------File1
 --------Sub-Directory-12019.zip
 ---Sub-Directory-2
 --------File1
 --------Sub-Directory-22019.zip

Я пробовал вложенный цикл for, но не могу его получить. Я пробовал команду for с пропуском и dir в наборе (IN), но не могу заставить ее работать.

В настоящее время у меня есть следующий сценарий.

SET theYear=2019
For /D %%d in (*.*) do 7z a "%%d\%%d%theYear%.zip" ".\%%d\*"

Это выполняет все, за исключением того, что я не знаю, как исключить последний файл (самый новый файл в соответствии с временем последнего изменения) в каждой папке.


person skatercurt    schedule 22.08.2019    source источник
comment
очень хороший первый вопрос! И очень хорошая работа по иллюстрации вашей проблемы с упрощенной картиной проблемы. Но... ;-) ... нам нужно увидеть имя файла, которое больше соответствует последнему файлу. Вам нужно будет временно скрыть его (переименовать как file1.HID, а затем использовать for /D %%d in (*.txt)... (просто пример) или придумать другой способ пропустить самый новый файл. Придумать способ программной идентификации этого файла - суть вашей проблемы. и вам было бы неплохо добавить информацию в свой вопрос, чтобы мы могли помочь.Все ИМХО, удачи!   -  person shellter    schedule 22.08.2019
comment
Кхм, @aschipfl нашел еще один способ изолировать новейший файл. Я не думал, что это возможно в .bat кодировании, но рад, что ошибся! Всем удачи.   -  person shellter    schedule 22.08.2019
comment
@shelter, Спасибо за похвалу моего первого вопроса :) Файлы все одного типа (расширения), но с разными именами. Я никогда не думал о том, чтобы скрыть это точно, но я играл с идеей перемещения всего, кроме самого последнего ... и здесь та же проблема, идентификация последнего в пакете.   -  person skatercurt    schedule 22.08.2019


Ответы (2)


Этот командный файл можно использовать для этой задачи:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FilesToIgnore=1"
set "theYear=%DATE:~-4%"
set "ListFile=%Temp%\%~n0.lst"
del "%ListFile%" 2>nul
for /D %%I in (*) do call :CompressFiles "%%I"
goto EndBatch

:CompressFiles
pushd %1
set "ZipFile=%~nx1%theYear%.zip"
for /F "skip=%FilesToIgnore% eol=| delims=" %%J in ('dir /A-D /B /O-D /TW 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /V /X /C:"%ZipFile%"') do >>"%ListFile%" echo %%J
if exist "%ListFile%" (
    echo Compressing files in directory %1 ...
    7z.exe a -bd -bso0 -i"@%ListFile%" -mx9 -scsDOS -- "%ZipFile%"
    del "%ListFile%"
)
popd
goto :EOF

:EndBatch
endlocal

Пакетный файл устанавливает переменную среды theYear динамически из зависящей от региона строки даты динамической переменной среды DATE. Пожалуйста, выполните в окне командной строки echo %DATE:~-4% и проверьте, является ли вывод текущим годом, потому что echo %DATE% выводит текущую локальную дату с последними четырьмя символами, являющимися годом.

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

Пожалуйста, прочтите справку по 7-Zip, чтобы узнать об используемых переключателях и параметрах.

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

  • del /?
  • dir /?
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • goto /?
  • if /?
  • popd /?
  • pushd /?
  • set /?
  • setlocal /?

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

Обновление: 7-Zip версии 19.00.0.0 выводит предупреждение и создает пустой ZIP-файл в каждом подкаталоге при использовании командной строки, как описано ниже, и первоначально использовалось в пакетном файле. Сначала я подумал, что это ошибка 7-Zip версии 19.00.0.0, потому что эта версия должна также поддерживать -- согласно ее справке, а 7-Zip версия 16.04.0.0 работает. при использовании командной строки:

7z.exe a -bd -bso0 -mx9 -scsDOS -- "%ZipFile%" "@%ListFile%"

Необходимо было удалить -- из этой командной строки, чтобы заставить ее работать с 7-Zip версии 19.00.0.0.

Итак, я сообщил об этой проблеме, и автор 7-Zip быстро ответил, объяснив, почему использование -- приводит к поиску файла, имя которого начинается с @, начиная с 7-Zip версии 19.00.0.0:

Нам нужен какой-то способ добавить имена @file и -file из командной строки.
Таким образом, -- останавливает анализ @ и -, а 7-Zip ищет точные имена.
Вместо этого более безопасно использовать -i@listfile.

Поэтому я обновил код пакетного файла и указал файл списка с опцией -i, которая является наиболее безопасным методом для указания файла списка.

person Mofi    schedule 22.08.2019
comment
Спасибо @Мофи! Сначала это не сработало. Я удалил -bd -bso0 -mx9 -scsDOS -- часть команды 7z, и это помогло. Удивительно! Спасибо еще раз. Для протокола и для всех, кто ищет что-то подобное, решение aschipfl также работает. - person skatercurt; 22.08.2019
comment
Один добавленный вопрос @Mofi. Есть ли способ заставить его проходить каталоги по порядку (в алфавитном порядке)? Я добавил паузу и эхо при тестировании, чтобы посмотреть, что он делает. Я хотел попробовать это на реальных файлах, для которых мне это нужно, и медленно пройтись по ним в первый раз, но на первом этапе он выбрал случайную папку. - person skatercurt; 23.08.2019
comment
@skatercurt for /D %%I in (*) do приводит к обработке подкаталогов в порядке, возвращаемом файловой системой. В разделах NTFS порядок зависит от языкового стандарта в алфавитном порядке. На дисках FAT16, FAT32 и exFAT порядок вообще не сортируется по файловой системе, потому что файловые системы FAT вообще не сортируют записи файлов. Можно использовать for /F "eol=| delims=" %%I in ('dir /AD-H /B /ON 2^>nul') do для первой командной строки for для обработки нескрытых подкаталогов в текущем каталоге, упорядоченных по именам, отсортированным по dir независимо от порядка сортировки файловой системы. - person Mofi; 23.08.2019
comment
Еще раз спасибо за вашу помощь! Вы действительно знаете свое дело. Он показывает индикатор выполнения без -bd, и если я добавлю его обратно, он исчезнет, ​​так что вы правы. -mx9 был слишком медленным для меня, поэтому я установил его на 5. Я добавил findstr в первый цикл for с переключателем /g, чтобы я мог создать текстовый файл с именами папок, которые я хочу исключить из обработки. . Еще раз спасибо за все, это сэкономит мне много времени (и места :)) - person skatercurt; 23.08.2019

Следующий код полностью не тестировался, поэтому будьте осторожны при выполнении:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "ROOT=Directory-Parent" & rem /* (set this to `%~dp0.` to specify the parent directory of the
                              rem     batch file, or to `.` to for current working directory) */
set "YEAR=2019"  & rem // (set the year just as a constant as it is unclear where to get it from)
set "MOVA="      & rem // (set this to `-sdel` if you want the files to be moved into the archive)

rem // Interate through the current working directory:
for /D %%d in ("%ROOT%\*.*") do (
    rem // Clear exclusion parameter for `7z`, reset flag that indicates if there are files left:
    set "EXCL=" & set "FLAG="
    rem /* Iterate through all files in the currently iterated directory, sorted from 
    rem    oldest to newest (`/O:D`), regarding the last modification date/time (`/T:W`);
    rem    the `findstr` part is to exclude the archive file itself if already present: */
    for /F "delims= eol=|" %%f in ('
        dir /B /A:-D /O:D /T:W "%%~d\*.*" ^| findstr /V /I /X /C:"%%~nxd%YEAR%.zip"
    ') do (
        rem // Exclusion parameter is not yet set in first loop iteration:
        if defined EXCL set "FLAG=#"
        rem // Exclusion parameter becomes set at this point:
        set "EXCL=-x!"%%f""
    )
    rem /* Flag is only set if there is one more file besides the one to exclude,
    rem    meaning that there are at least two files in total: */
    if defined FLAG (
        rem // Store folder information in variables to avoid loss of potentially occurring `!`:
        set "FOLDER=%%~fd" & set "NAME=%%~nxd"
        rem /* Toggle delayed expansion in order to be able to read variables that have been
        rem    set within the same block of parenthesised code (namely the `for` loops): */
        setlocal EnableDelayedExpansion
        rem // Execute the `7z` command line with dynamic parameters:
        ECHO 7z a -bd %MOVA% !EXCL! -x^^!"!NAME!%YEAR%.zip" "!FOLDER!\!NAME!%YEAR%.zip" "!FOLDER!\*.*"
        endlocal
    )
)

endlocal
exit /B

Перед (потенциально опасной) командной строкой 7z я поставил ECHO просто в целях безопасности.

person aschipfl    schedule 22.08.2019
comment
Похоже, теперь, когда он запускается во второй раз, он создает почтовый индекс внутри первого почтового индекса. И затем, если он запускается в третий раз, он создает почтовый индекс во втором почтовом индексе, который находится в первом почтовом индексе и так далее. - person skatercurt; 23.08.2019
comment
О, кажется, я не очень понимаю вариант -x!; возможно, нужно указать полный путь к .zip или что-то в этом роде? как -x^^!"!FOLDER!\!NAME!%YEAR%.zip"? - person aschipfl; 25.08.2019