使用 7zip 提取嵌套 zip 并在成功提取后删除 zip 的批处理文件

Batch file using 7zip to extract nested zips and delete zips after successful extraction

我有一个装满 zip 文件的文件夹。这些 zip 文件有时包含 zip 文件,有时又包含 zip 文件,等等。我正在尝试编写一个批处理文件,我可以将其粘贴到包含所有 zip 的顶级文件夹中,当它 运行s 时,它将解压缩所有嵌套的 zip 文件,并在子目录中,一直向下,并在成功提取后删除 zip。需要保留完整的文件路径。如果出现错误并且无法提取文件,则不应删除该文件,并且需要将文件和文件路径打印到文本文件中。

到目前为止我有这个:

@ECHO ON

SET source=%cd%
FOR /F "TOKENS=*" %%F IN ('DIR /S /B "%source%\*.zip"') DO "C:\Program Files-Zipz.exe" x "%%~fF" -o"%%~pF\"
EXIT

我可以将其放入一个文件夹,然后 运行,它会解压缩第一级 zips,但 none 里面的嵌套 zips。这是第一个障碍。

下一个障碍是删除成功提取的 zip。最后,不要删除任何无法提取的 zip,并将它们的名称 and/or 打印到文本文件的路径。

欢迎任何建议或代码块。或者如果有更好的方法来完全做到这一点。

**** 已更新 ****

Mofi 发布了一个看起来有效的答案,除了一个:

ZIP解压时,需要解压到同名文件夹,所以我还是按照结构来的。

起始示例:

[Top Level Folder Holding Zips] (folder)
--ExampleZip.zip
---FileInZip.txt
---FileinZip2.txt
--ExampleZip2.zip
---Folder1 (folder)
----ExampleZip3.zip
-----FileinZip3.txt
-----FileinZip4.txt
---ExampleZip4.zip
----FileinZip5.txt
----FileinZip6.txt

需要变成这样:

[Top Level Folder Holding Zips] (folder)
--ExampleZip (folder)
---FileInZip.txt
---FileinZip2.txt
--ExampleZip2 (folder)
---Folder1 (folder)
----ExampleZip3 (folder)
-----FileinZip3.txt
-----FileinZip4.txt
---ExampleZip4 (folder)
----FileinZip5.txt
----FileinZip6.txt

所以完整的结构仍然可见。

我认为这个问题的最佳答案显示了我需要包括的内容:

这部分:


SET "filename=%~1"
SET dirName=%filename:~0,-4%

7z x -o"%dirName%" "%filename%"

需要在某处砸碎。或者似乎应该有一个 7Zip 的开关来执行它,因为您可以从上下文菜单中使用“提取到 *”来执行此操作我认为这就是“使用完整路径提取”命令的作用,但必须有一些事情要做使用 -o 开关,指定输出路径?如何将输出路径指定为与输入 zip 同名的文件夹?或者合并我与 Mofi 的答案相关联的那个问题的答案?

*** 再次更新 ***

我认为批处理文件忽略名称中带下划线的 ZIP 文件有问题,但这是巧合,它实际上忽略了没有设置存档文件属性的 ZIP 文件。

Mofi 建议了另一种解决方法,但该方法有效,但批处理文件未提取需要存档文件属性集的嵌套 zip。

这确实有点用,因为我可以手动执行批处理文件几次,它会遍历文件夹中的所有内容,但循环计算似乎不起作用,或者 calculating/terminating 在批处理文件之前为所有 zip 文件设置存档属性?

这是我正在使用的当前版本:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ErrorOutput="
set "LoopCount=20"

rem The current directory is used on batch file being called without
rem a base folder path or with just one or more double quotes.
set "BaseFolder=%~1"
if defined BaseFolder set "BaseFolder=%BaseFolder:"=%"
if not defined BaseFolder set "BaseFolder=%CD%" & goto VerifyFolderPath

rem Make sure the folder path contains backslashes and not forward slashes
rem and does not contain wildcard characters or redirection operators or a
rem horizontal tab character after removing all double quotes.
set "BaseFolder=%BaseFolder:/=\%"
for /F "delims=*?|<>    " %%I in ("%BaseFolder%") do if not "%BaseFolder%" == "%%I" (
    echo ERROR: %~nx0 must be called with a valid folder path.
    echo        "%~1" is not a valid folder path.
    set "ErrorOutput=1"
    goto EndBatch
)

rem Get full folder path in case of the folder was specified with
rem a relative path. If the folder path references the root of a
rem drive like on using "C:\" or just "\", redefine the folder
rem path with full path for root of the (current) drive.
for %%I in ("%BaseFolder%") do set "BaseFolder=%%~fI"

:VerifyFolderPath
rem The base folder path must end with a backslash for verification.
if not "%BaseFolder:~-1%" == "\" set "BaseFolder=%BaseFolder%\"

rem Verify the existence of the folder. The code above processed also
rem folder paths of folders not existing at all and also invalid folder
rem paths containing for example a colon not (only) after drive letter.
if not exist "%BaseFolder%" (
    echo ERROR: Folder "%BaseFolder%" does not exist.
    set "ErrorOutput=1"
    goto EndBatch
)

rem Make sure to process all ZIP files existing in base folder and all
rem its subfolders by setting archive file attribute on all ZIP files.
%SystemRoot%\System32\attrib.exe +A /S "%BaseFolder%*.zip"

rem Process all *.zip files found in base folder and all its subfolders
rem which have the archive file attribute set. *.zip files with archive
rem file attribute not set are ignored to avoid an endless running loop
rem if a ZIP archive file cannot be extracted successfully with reason(s)
rem output by 7-Zip or if the ZIP file cannot be deleted after successful
rem extraction of the archive. The archive extraction loop runs are limited
rem additionally by a loop counter as defined at top of the batch file for
rem 100% safety on prevention of an endless loop execution.

:ExtractArchives
set "ArchiveProcessed="
for /F "delims=" %%I in ('dir "%BaseFolder%*.zip" /AA-D /B /S 2^>nul') do (
    set "ArchiveProcessed=1"
    echo Extracting archive: "%%I"
    "%ProgramFiles%-Zipz.exe" x -bd -bso0 -o"%%~dpnI\" -spd -y -- "%%I"
@pause
    if errorlevel 255 set "ErrorOutput=1" & goto EndBatch
    if errorlevel 1 (
        set "ErrorOutput=1"
        %SystemRoot%\System32\attrib.exe -A "%%I"
    ) else (
        del /A /F "%%I"
        if exist "%%I" (
            echo ERROR: Failed to delete: "%%I"
            set "ErrorOutput=1"
            %SystemRoot%\System32\attrib.exe -A "%%I"
        )
    )
)
if not defined ArchiveProcessed goto EndBatch
set /A LoopCount-=1
if not LoopCount == 0 goto ExtractArchives

:EndBatch
if defined ErrorOutput echo/& pause
endlocal
echo[
echo[
echo If no errors are displayed above, everything extracted successfully. Remember to delete the batch file once you are done.
@pause

很少有 10 或 20 层嵌套 zip,因此快速而肮脏的修复可能只是以某种方式将整个批处理文件循环 10 或 20 次,除非这是个坏主意或有一种更优雅的方式。

使用 Groovy 或 Ant

使用 Apache Ant or, better still, the Groovy AntBuilder 会容易得多。

例如此 Groovy 脚本将解压缩所有顶级 zip 文件,然后将其删除:

new AntBuilder().with {

  def sourceRoot = '.'

  // Unzip all .zip files in / underneath sourceRoot
  unzip( dest: 'some-folder' ) {
    fileset( dir: sourceRoot ) {
      include name: "**/*.zip"
    }
  }

  // Unzip throws an exception on failure.
  // Delete all .zip files in / underneath sourceRoot
  delete {
    fileset( dir: sourceRoot, includes: '**/*.zip' )
  }
}

您需要继续扫描目标文件夹中的 zip,并重复上述过程,直到所有内容都解压缩。您可能还会发现使用 FileScanner 很有用。

AntBuilder 如果失败则抛出异常,因此您可以避免删除无法解压缩的存档。 AntBuilder 还将使用标准 Java 日志记录机制记录它的进度。你可以告诉它你想要的详细程度,或者完全抑制它

完整的 AntBuilder 文档在这里:

使用文件扫描器

来自 Groovy AntBuilder 文档的示例:

// let's create a scanner of filesets
def scanner = ant.fileScanner {
    fileset(dir:"src/test") {
        include(name:"**/My*.groovy")
    }
}

// now let's iterate over
def found = false
for (f in scanner) {
    println("Found file $f")
    found = true
    assert f instanceof File
    assert f.name.endsWith(".groovy")
}
assert found

放在一起

将 filesScanner 与 AntBuilder 结合起来完成工作并不是一个巨大的飞跃。我怀疑这比使用批处理脚本要容易很多

递归提取所有 ZIP 档案(包括 ZIP 档案中的嵌套 ZIP 档案)的任务可以通过 运行循环 ZIP 档案文件提取过程直到不再存在 ZIP 文件来实现。但是必须至少考虑两个用例,以避免无休止的 运行ning 存档提取循环:

  1. 无论出于何种原因,ZIP 存档文件的提取都失败了。 7-Zip 输出有关错误原因的信息。这样的 ZIP 文件不应该进行第二次处理。
  2. 无论出于何种原因,成功提取的 ZIP 文件删除失败。不应再次处理 ZIP 文件。

该解决方案仅处理归档文件属性设置为 Windows 在创建、重命名或修改文件时自动完成的 ZIP 文件,并删除每个 ZIP 文件的归档文件属性,提取过程或删除文件失败避免再次处理ZIP文件。

存档文件属性设置在目录树上的所有*.zip 文件上,以便在开始存档文件提取过程之前进行处理,以确保确实所有现有的*.zip 文件至少被处理一次。在完全成功处理的 ZIP 存档文件的输出目录中的所有 *.zip 文件上也设置了存档文件属性,以确保即使在提取后未设置存档文件属性的 ZIP 文件中的 *.zip 文件也在下一次处理存档文件提取循环 运行.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ErrorOutput="
set "LoopCount=20"

rem The current directory is used on batch file being called without
rem a base folder path or with just one or more double quotes.
set "BaseFolder=%~1"
if defined BaseFolder set "BaseFolder=%BaseFolder:"=%"
if not defined BaseFolder set "BaseFolder=%CD%" & goto VerifyFolderPath

rem Make sure the folder path contains backslashes and not forward slashes
rem and does not contain wildcard characters or redirection operators or a
rem horizontal tab character after removing all double quotes.
set "BaseFolder=%BaseFolder:/=\%"
for /F "delims=*?|<>    " %%I in ("%BaseFolder%") do if not "%BaseFolder%" == "%%I" (
    echo ERROR: %~nx0 must be called with a valid folder path.
    echo        "%~1" is not a valid folder path.
    set "ErrorOutput=1"
    goto EndBatch
)

rem Get full folder path in case of the folder was specified with
rem a relative path. If the folder path references the root of a
rem drive like on using "C:\" or just "\", redefine the folder
rem path with full path for root of the (current) drive.
for %%I in ("%BaseFolder%") do set "BaseFolder=%%~fI"

:VerifyFolderPath
rem The base folder path must end with a backslash for verification.
if not "%BaseFolder:~-1%" == "\" set "BaseFolder=%BaseFolder%\"

rem Verify the existence of the folder. The code above processed also
rem folder paths of folders not existing at all and also invalid folder
rem paths containing for example a colon not (only) after drive letter.
if not exist "%BaseFolder%" (
    echo ERROR: Folder "%BaseFolder%" does not exist.
    set "ErrorOutput=1"
    goto EndBatch
)

rem Make sure to process all ZIP files existing in base folder and all
rem its subfolders by setting archive file attribute on all ZIP files.
%SystemRoot%\System32\attrib.exe +A /S "%BaseFolder%*.zip" >nul

rem Process all *.zip files found in base folder and all its subfolders
rem which have the archive file attribute set. *.zip files with archive
rem file attribute not set are ignored to avoid an endless running loop
rem if a ZIP archive file cannot be extracted successfully with reason(s)
rem output by 7-Zip or if the ZIP file cannot be deleted after successful
rem extraction of the archive. The archive extraction loop runs are limited
rem additionally by a loop counter as defined at top of the batch file for
rem 100% safety on prevention of an endless loop execution.

:ExtractArchives
set "ArchiveProcessed="
for /F "delims=" %%I in ('dir "%BaseFolder%*.zip" /AA-D /B /S 2^>nul') do (
    set "ArchiveProcessed=1"
    echo Extracting archive: "%%I"
    "%ProgramFiles%-Zipz.exe" x -bd -bso0 -o"%%~dpI" -spd -y -- "%%I"
    if errorlevel 255 set "ErrorOutput=1" & goto EndBatch
    if errorlevel 1 (
        set "ErrorOutput=1"
        %SystemRoot%\System32\attrib.exe -A "%%I"
    ) else (
        %SystemRoot%\System32\attrib.exe +A /S "%%~dpnI\*.zip" >nul
        del /A /F "%%I"
        if exist "%%I" (
            echo ERROR: Failed to delete: "%%I"
            set "ErrorOutput=1"
            %SystemRoot%\System32\attrib.exe -A "%%I"
        )
    )
)
if not defined ArchiveProcessed goto EndBatch
set /A LoopCount-=1
if not LoopCount == 0 goto ExtractArchives

:EndBatch
if defined ErrorOutput echo/& pause
endlocal

注意:批处理文件代码第16行"delims=*?|<>"后必须有一个水平制表符,不能是一连串的space 个字符,因为从浏览器 window 复制代码并将代码粘贴到文本编辑器 window。

批处理文件用命令REM(备注)的行进行注释。应该阅读这些注释以理解代码,然后可以删除这些注释以便 Windows 命令处理器更有效地执行批处理文件。

代码中使用的 7-Zip 开关在 7-Zip 的帮助下解释,双击文件 [=14] 打开=] 或从启动 7-Zip 的 GUI window 中打开 Help。在帮助选项卡 Contents 展开列表项 Command Line Version 并单击列表项 Switches 以显示帮助页面 命令行开关 包含当前使用的 7-Zip.

版本支持的所有开关

可以使用文件夹路径作为参数执行批处理文件,以处理此文件夹及其所有子文件夹中的所有 ZIP 文件。因此,可以在 Windows 文件资源管理器 发送到 上下文菜单中添加一个快捷方式文件,其中 运行批处理文件,文件夹路径由 Windows File Explorer 作为第一个参数传递给批处理文件。也可以在 Windows 注册表中将批处理文件注册为 Directory 的上下文菜单选项,以便能够从支持 Windows 的任何应用程序中轻松地 运行 批处理文件] 目录的上下文菜单处理程序。

问题编辑后编辑:命令行运行ning 7-Zip可修改为:

"%ProgramFiles%-Zipz.exe" x -bd -bso0 -o"%%~dpnI\" -spe -spd -y -- "%%I"

由于 -o"%%~dpI"-o"%%~dpnI\" 替换,因此使用此命令行将每个 ZIP 文件提取到 ZIP 文件文件夹中的子文件夹中,该子文件夹具有 ZIP 文件的名称。如果 ZIP 文件在顶层包含与 ZIP 文件同名的文件夹,则附加 7-Zip 开关 -spe 可避免重复文件夹名称。因此,如果 Example3.zip 在顶层包含文件夹 Example3,文件将被提取到文件夹 Example3 而不是文件夹 Example3\Example3,因为如果不使用选项 [=18] 就会发生这种情况=].

要了解使用的命令及其工作原理,请打开 command prompt window,在其中执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。

  • attrib /?
  • call /?
  • dir /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • rem /?
  • set /?
  • setlocal /?

阅读有关 Using command redirection operators 的 Microsoft 文档,了解 2>nul 的解释。重定向运算符 > 必须在 上使用脱字符 ^ 进行转义,以便在 Windows 命令解释器处理此命令时将其解释为文字字符 命令行执行命令 FOR 之前的行,它在后台启动的单独命令进程中执行嵌入式 dir 命令行。

终于设法编写了一个可以解压嵌套 zips 的批处理文件,同时保持存档文件结构完整!

逻辑是,运行 递归直到所有 zip 文件都解压缩。迭代次数默认为 5,可以作为 cmd arg“extract.bat 3”传递。可以改成while循环直到hit file not found异常。最重要的是在提取后删除存档文件,这样我们就不会陷入死循环! 但请遵守以下规则

  1. 它使用7z,确保在cmd window 7z可以是运行,也就是在路径
  2. zip 文件名不能有空格。确保这一点并且分机是 zip
  3. 将 zip 文件复制到没有其他 zip 文件的目录
  4. 并且只有 .zip ext,您可以将其更改为 rar 或批处理文件中的任何内容

这是批处理文件


Rem Nested unzip - @sivakd
echo off
if  "%1"=="" (set iter=5) else (set iter=%1)
echo Running  %iter% iterations
for /l %%x in (1, 1, %iter%) do (
    dir *.zip /s /b > ziplist.txt
    for /F %%f in (ziplist.txt) do (
        7z x %%f -o%%~dpnf -y & del /f %%f
    )
    del ziplist.txt
)
</pre>