如何将文件夹的每个子文件夹中除最新文件外的所有文件压缩为每个子文件夹一个 ZIP 文件?
Posted
技术标签:
【中文标题】如何将文件夹的每个子文件夹中除最新文件外的所有文件压缩为每个子文件夹一个 ZIP 文件?【英文标题】:How to compress all files with exception of newest file in each subfolder of a folder into one ZIP file per subfolder? 【发布时间】:2019-08-22 16:48:29 【问题描述】:我正在尝试创建一个批处理脚本,它将压缩每个子目录中的所有内容,除了最新的(或最新的少数)。我目前正在使用 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
循环,但似乎无法得到它。我已经在集合 (IN) 中尝试了带有 skip 和 dir
的 for
命令,但无法使其正常工作。
我目前有以下脚本。
SET theYear=2019
For /D %%d in (*.*) do 7z a "%%d\%%d%theYear%.zip" ".\%%d\*"
这一切都完成了,除了我不知道如何排除每个文件夹中的最新文件(根据上次修改时间的最新文件)。
【问题讨论】:
非常好的第一Q!并且很好地用简化的问题“图片”来说明您的问题。但是... ;-) ...我们需要看到一个更能代表最新文件的文件名。你需要暂时隐藏它(重命名为file1.HID
,然后使用for /D %%d in (*.txt)...
(只是一个例子),或者想出另一种跳过最新文件的方法。想出一种以编程方式识别这个文件的方法是您的问题的症结所在,您最好将信息添加到您的 Q 中,以便我们提供帮助。恕我直言,祝您好运!
咳咳,@aschipfl 找到了另一种隔离最新文件的方法。我不认为在.bat
编码中是可能的,但很高兴被证明是错误的!祝大家好运。
@shelter,感谢您对我的第一个问题的称赞 :) 这些文件都是相同类型(扩展名)但名称不同。我从来没有想过完全“隐藏它”,但我玩弄了移动除最新之外的所有内容的想法......而且存在同样的问题,在批次中识别最新的。
【参考方案1】:
此批处理文件可用于此任务:
@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
批处理文件根据动态环境变量DATE
的区域相关日期字符串动态设置环境变量theYear
。请在命令提示符窗口中执行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 /?
阅读有关Using Command Redirection Operators 的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 一起使用。
所以我reported this issue 和 7-Zip 的作者很快回复解释为什么使用 --
会导致在文件名中搜索以 @
开头的文件,因为 7-Zip版本 19.00.0.0:
我们需要一些方法来从命令行添加
@file
和-file
名称。 所以--
停止@
和-
解析,7-Zip 搜索确切的名称。 改用-i@listfile
更安全。
所以我更新了批处理文件代码并使用选项-i
指定列表文件,这是指定列表文件的最安全方法。
【讨论】:
谢谢@Mofi!起初这不起作用。我删除了 7z 命令的“-bd -bso0 -mx9 -scsDOS --”部分,就成功了。惊人的!再次感谢。为了记录和寻找类似 aschipfl 解决方案的任何其他人也可以使用。 添加了一个问题@Mofi。有没有办法让它按顺序(按字母顺序)浏览目录?我在测试中添加了暂停和回声以观察它在做什么。我想在我需要它的真实文件上尝试它,并在第一次慢慢地浏览它,但第一步它选择了一个随机文件夹。 @skatercurtfor /D %%I in (*) do
导致按文件系统返回的顺序处理子目录。在 NTFS 分区上,顺序是特定于语言环境的字母顺序。在 FAT16、FAT32 和 exFAT 驱动器上,文件系统根本不会对顺序进行排序,因为 FAT 文件系统根本不会对文件条目进行排序。可以在第一个 for 命令行中使用for /F "eol=| delims=" %%I in ('dir /AD-H /B /ON 2^>nul') do
来处理当前目录中的非隐藏子目录,这些子目录按名称排序,按dir
排序,与文件系统排序顺序无关。
再次感谢您的帮助!你真的很了解你的东西。它确实显示了没有-bd
的进度指示,如果我把它加回去,它就会消失,所以你是对的。 -mx9 对我来说很慢,所以我将它设置为 5。我使用 /g 开关将 findstr 添加到了第一个 for 循环中,这样我就可以创建一个我想要从处理中排除的文件夹名称的文本文件.再次感谢您所做的一切,这将为我节省大量时间(和空间:))【参考方案2】:
以下代码完全未经测试,执行时请注意:
@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
为了安全起见,我在ECHO
前面(可能很危险)7z
命令行。
【讨论】:
第二次运行时,它会在第一个 zip 中创建一个 zip。然后,如果它第三次运行,它会在第一个 zip 中的第二个 zip 中创建一个 zip,依此类推。 哦,看来我不太明白-x!
选项;也许有必要指定.zip
的完整路径之类的?喜欢-x^^!"!FOLDER!\!NAME!%YEAR%.zip"
?以上是关于如何将文件夹的每个子文件夹中除最新文件外的所有文件压缩为每个子文件夹一个 ZIP 文件?的主要内容,如果未能解决你的问题,请参考以下文章