如何使用 WinRAR 命令行将多个压缩文件解压到每个文件夹?

How can I extract multiple archives to each folder with WinRAR command line?

我想在命令行中将每个文件夹中的 ZIP 和 RAR 压缩文件解压到相应的文件夹中。另外,我只想在解压过程中没有错误的情况下删除原始压缩文件。如果解压存档时出错,存档文件名应写入错误日志文件,解压过程应继续处理下一个存档文件。

解压成功后,我想将所有文件夹移动到done文件夹中。但是不包含任何存档文件的文件夹不应被批处理文件移动。

之前:

C:
│
└─test
    ├─AAAA
    │      XXXX.rar
    │      XXXX.jpg
    │
    ├─BBBB
    │      XXXX.zip
    │      XXXX.jpg
    │
    ├─CCCC(error_file)
    │      XXXX.rar
    │      XXXX.jpg
    │
    ├─DDDD
    │      XXXX.part1.rar
    │      XXXX.part2.rar
    │      XXXX.jpg
    │
    └─EEEE
           XXXX.jpg

之后:

C:
│
└─test
    ├─done
    │  │
    │  │
    │  ├─AAAA
    │  │      XXXX.doc
    │  │      XXXX.jpg
    │  │
    │  ├─BBBB
    │  │      XXXX.doc
    │  │      XXXX.jpg
    │  │
    │  └─DDDD
    │         XXXX.doc
    │         XXXX.jpg
    │
    ├─CCCC(error_file)
    │      XXXX.rar
    │      XXXX.jpg
    │
    └─EEEE
           XXXX.jpg


以下代码摘自 Mofi 对问题初始版本的回答并由我改编,但无法正常工作。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=C:\test"
set "LogExtract=%SourceFolder%\ExtractionLog.txt"
set "LogError=%SourceFolder%\ErrorLog.txt"
set "ArchiveExtracted="

del /Q "%LogExtract%" "%LogError%" 2>nul

for /D %%I in ("%SourceFolder%\*") do (
    if /I not "%%~nxI" == "done" (
        for /F "eol=| delims=" %%J in ('dir "%%I\*.rar" "%%I\*.zip" /A-D-H /B /ON 2^>nul') do (
            if exist "%%I\%%J" (
                echo Extracting "%%I\%%J" ...
                "%ProgramFiles%\WinRAR\WinRAR.exe" x -cfg- -logpfu="%LogExtract%" -or -- "%%I\%%J" "%%I\"
                if errorlevel 1 (
                    set "ArchiveFile=%%I\%%J"
                    >>"%LogError%" call echo Error %%ErrorLevel%% on extracting "%%ArchiveFile%%"
                ) else (
                    echo %%~nJ| %SystemRoot%\System32\findstr.exe /I /R "\.part[0123456789][0123456789]*$" >nul
                    if errorlevel 1 ( del /F "%%I\%%J" ) else for %%# in ("%%~nJ") do del /F /Q "%%I\%%~n#.part*%%~xJ"
                )
            )
        )
        if /I not "%%~nxI" == "done" if not exist "%%I\*.rar" if not exist "%%I\*.zip" move /Y "%%I" "%SourceFolder%\done\"
    )
)


endlocal

Rar.exe 仅支持 WinRAR 的程序文件文件夹中其手册 Rar.txt 顶部记录的 RAR 压缩文件。 WinRAR.exe 支持创建 RAR 和 ZIP 压缩文件以及提取多种压缩文件类型。因此下面的批处理文件代码中使用了WinRAR.exe

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=C:\Test"
set "LogExtract=%SourceFolder%\ExtractionLog.txt"
set "LogError=%SourceFolder%\ErrorLog.txt"
set "ArchiveExtracted="

del /Q "%LogExtract%" "%LogError%" 2>nul

for /D %%I in ("%SourceFolder%\*") do (
    if /I not "%%~nxI" == "done" (
        for %%J in ("%%I\*.rar" "%%I\*.zip") do (
            if exist "%%J" (
                echo Extracting "%%J" ...
                "%ProgramFiles%\WinRAR\WinRAR.exe" x -cfg- -ibck -logpfu="%LogExtract%" -o+ -y -- "%%J" "%%I\"
                if errorlevel 1 (
                    set "ArchiveFile=%%J"
                    >>"%LogError%" call echo Error %%ErrorLevel%% on extracting "%%ArchiveFile%%"
                ) else (
                    set "#%%~nxI=%%I"
                    set "ArchiveExtracted=1"
                    echo %%~nJ| %SystemRoot%\System32\findstr.exe /I /R "\.part[0123456789][0123456789]*$" >nul
                    if errorlevel 1 ( del /F "%%J" ) else for %%# in ("%%~nJ") do del /F /Q "%%I\%%~n#.part*%%~xJ"
                )
            )
        )
    )
)

if defined ArchiveExtracted (
    md "%SourceFolder%\done" 2>nul
    if exist "%SourceFolder%\done\" (
        for /F "tokens=2 delims==" %%I in ('set #') do move /Y "%%I" "%SourceFolder%\done\"
    )
)

endlocal

在定义了父源文件夹并从之前的执行中删除了可能已经存在的日志文件之后,外部 FOR 在指定的源文件夹中搜索非隐藏子目录。

对于每个找到的子目录,除了名称为 done 的子目录,内部 FOR 在子目录中搜索非隐藏的 *.rar 和 *.zip 文件并执行WinRAR.exe 将找到的每个存档文件提取到子目录中。

WinRAR 提取每个压缩文件

  • 保持目录结构,
  • 忽略标准配置,
  • 在后台,这意味着最小化到系统托盘,
  • 记录从 RAR 压缩文件到 Unicode 编码(UTF-16 Little Endian 无 BOM)提取日志文件,
  • 覆盖所有已经存在的文件,
  • 假设 对于所有查询,例如错误。

WinRAR 自动提取多卷压缩包的所有卷。

WinRAR 大于或等于 1 的值退出 的帮助中记录的错误]WinRAR 帮助页面 WinRAR 退出代码列表

存档文件名分配给环境变量 ArchiveFile 以防 WinRAR 退出时未 100% 成功提取存档文件,值为 0 下一个错误消息行是输出,将 WinRAR 的退出代码和文件名写入错误日志文件。错误日志文件根据 Windows 命令处理器在启动批处理文件时定义的字符编码和代码页进行编码。

环境变量 ErrorLevelArchiveFile 每边都用两个百分号引用,因为 Windows 命令处理器在执行外部 [=105] 之前已经在解析整个命令块时替换=]FOR all %% by just %.命令 CALL 导致在执行 ECHO 之前对 ECHO 命令行进行第二次解析,这导致替换%Errorlevel% 按此环境变量的当前值以及 %ArchiveFile% 按当前存档文件名。

echo Error %ErrorLevel% on extracting "%ArchiveFile%" 这样的 ECHO 行没有命令 CALL 会导致用当前值替换 %ErrorLevel%环境变量 ErrorLevel 在执行外部 FOR 之前,这意味着使用 0 并将 %ArchiveFile% 替换为空字符串,这当然不会有帮助。

存档文件名已分配给环境变量 ArchiveFile 并像 ErrorLevel 一样被引用以处理像 Archive%20!Important!.rar 这样的文件名也是正确的。

在多卷压缩文件上出错 none RAR 压缩文件部分被删除,导致使用多卷压缩文件的每个部分多次提取多卷压缩文件的所有卷的方法文件写入错误日志文件。

成功提取单个档案文件后,将删除档案文件;成功提取多卷档案后,将删除多卷档案的所有部分。此外,环境变量设置为 # 加上子目录名称作为环境变量名称,子目录的完整路径作为值,以记住哪些子目录包含至少一个成功提取的存档以便稍后移动它。此简单方法要求子目录名称中不包含等号。

以上代码无法在子目录中的多个 *.rar 或 *.zip 文件的 FAT32 或 ExFAT 驱动器上运行。在这种情况下,有必要在后台以 %ComSpec% /C 启动的单独命令进程中使用由 FOR 执行的命令 DIR 并捕获输出存档文件名。然后内部 FOR 运行一个归档文件名列表,在循环迭代期间由于删除 FAT32 和 ExFAT 驱动器上的归档文件而未修改。

如果存档文件本身包含 *.rar 或 *.zip 文件,则也需要此备用批处理文件,这些文件不应被偶然提取,因为上面的批处理文件代码可能会发生这种情况。

所以第二批代码比第一批安全。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=C:\Test"
set "LogExtract=%SourceFolder%\ExtractionLog.txt"
set "LogError=%SourceFolder%\ErrorLog.txt"
set "ArchiveExtracted="

del /Q "%LogExtract%" "%LogError%" 2>nul

for /D %%I in ("%SourceFolder%\*") do (
    if /I not "%%~nxI" == "done" (
        for /F "eol=| delims=" %%J in ('dir "%%I\*.rar" "%%I\*.zip" /A-D-H /B /ON 2^>nul') do (
            if exist "%%I\%%J" (
                echo Extracting "%%I\%%J" ...
                "%ProgramFiles%\WinRAR\WinRAR.exe" x -cfg- -ibck -logpfu="%LogExtract%" -o+ -y -- "%%I\%%J" "%%I\"
                if errorlevel 1 (
                    set "ArchiveFile=%%I\%%J"
                    >>"%LogError%" call echo Error %%ErrorLevel%% on extracting "%%ArchiveFile%%"
                ) else (
                    set "#%%~nxI=%%I"
                    set "ArchiveExtracted=1"
                    echo %%~nJ| %SystemRoot%\System32\findstr.exe /I /R "\.part[0123456789][0123456789]*$" >nul
                    if errorlevel 1 ( del /F "%%I\%%J" ) else for %%# in ("%%~nJ") do del /F /Q "%%I\%%~n#.part*%%~xJ"
                )
            )
        )
    )
)

if defined ArchiveExtracted (
    md "%SourceFolder%\done" 2>nul
    if exist "%SourceFolder%\done\" (
        for /F "tokens=2 delims==" %%I in ('set #') do move /Y "%%I" "%SourceFolder%\done\"
    )
)

endlocal

注意: WinRAR 5.70版本不支持写入从ZIP[=145中提取的文件的文件名=] 存档到提取日志文件。这记录在帮助页面的顶部 Switch -LOG[fmt][=name] - 将名称写入日志文件.

最后,如果成功提取并删除了任何存档,批处理文件会将所有以 # 开头的环境变量存在的文件夹移动到父源目录中的子目录 done 中。因此,在文件夹移动时会忽略未成功提取至少一个存档的子目录以及没有存档文件的子目录。如果最终目标目录不是父源目录的子目录,代码可能会更简单。

还有一个变体,它在压缩包提取完成后立即移动文件夹。在这种情况下,有必要使用捕获的子目录名称列表,因为在外部 FOR 循环的循环迭代期间子目录列表会发生变化。 FINDSTR 用于从子目录名称列表中过滤掉文件夹 done

此批处理文件等待用户选择中断执行,并在两秒后自动选择 no。因此用户可以安全地中断批处理作业。这个提示可以通过启动带参数/noprompt.

的批处理文件来避免
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "PromptForBreak="
if /I "%~1" == "/noprompt" set "PromptForBreak=rem"

set "SourceFolder=C:\Test"
set "LogExtract=%SourceFolder%\ExtractionLog.txt"
set "LogError=%SourceFolder%\ErrorLog.txt"

del /Q "%LogExtract%" "%LogError%" 2>nul

for /F "eol=| delims=" %%I in ('dir "%SourceFolder%\*" /AD-H /B /ON 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /V /X /C:done') do (
    set "ArchiveExtracted="
    for /F "eol=| delims=" %%J in ('dir "%SourceFolder%\%%I\*.rar" "%SourceFolder%\%%I\*.zip" /A-D-H /B /ON 2^>nul') do (
        if exist "%SourceFolder%\%%I\%%J" (
            echo Extracting "%SourceFolder%\%%I\%%J" ...
            "%ProgramFiles%\WinRAR\WinRAR.exe" x -cfg- -ibck -logpfu="%LogExtract%" -o+ -y -- "%SourceFolder%\%%I\%%J" "%SourceFolder%\%%I\"
            if errorlevel 1 (
                set "ArchiveFile=%SourceFolder%\%%I\%%J"
                >>"%LogError%" call echo Error %%ErrorLevel%% on extracting "%%ArchiveFile%%"
            ) else (
                set "ArchiveExtracted=1"
                echo %%~nJ| %SystemRoot%\System32\findstr.exe /I /R "\.part[0123456789][0123456789]*$" >nul
                if errorlevel 1 ( del /F "%SourceFolder%\%%I\%%J" ) else for %%# in ("%%~nJ") do del /F /Q "%SourceFolder%\%%I\%%~n#.part*%%~xJ"
            )
        )
    )
    if defined ArchiveExtracted (
        md "%SourceFolder%\done" 2>nul
        if exist "%SourceFolder%\done\" move /Y "%SourceFolder%\%%I" "%SourceFolder%\done\"
        %PromptForBreak% %SystemRoot%\System32\choice.exe /C NY /N /T 2 /D N /M "Break execution [N/Y]? "
        %PromptForBreak% if errorlevel 2 goto EndBatch
    )
)

:EndBatch
endlocal

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

  • call /?
  • del /?
  • dir /?
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • if /?
  • md /?
  • move /?
  • set /?
  • setlocal /?