How can I extract multiple archives to each folder

2019-08-20 08:25发布

I want to extract ZIP and RAR archives existing in each folder to the respective folder at the command line. Furthermore, I want to delete the original compressed file only if there is no error during decompression. If there is an error on extracting an archive, the archive file name should be written to an error log file and extraction process should continue with the next archive file(s).

I want to move every folder to done folder after the decompression is successful. But folders not containing any archive file should not be moved by the batch file.

Before:

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

After:

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


The following code taken from Mofi's answer from initial version of the question and adapted by me did not work.

@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

1条回答
成全新的幸福
2楼-- · 2019-08-20 09:25

Rar.exe supports only RAR archives as documented at top of its manual Rar.txt in program files folder of WinRAR. WinRAR.exe supports creation of RAR and ZIP archives and extraction of multiple archive types. Therefore WinRAR.exe is used in batch file code below.

@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

After definition of parent source folder and deletion of perhaps already existing log files from a previous execution the outer FOR searches in specified source folder for non-hidden subdirectories.

For each found subdirectory except the one with name done the inner FOR searches for non-hidden *.rar and *.zip files in the subdirectory and executes WinRAR.exe to extract each found archive file into the subdirectory.

WinRAR extracts each archive file

  • with keeping directory structure,
  • with ignoring standard configuration,
  • in background which means minimized to system tray,
  • with logging extracted files from RAR archives to Unicode encoded (UTF-16 Little Endian without BOM) extraction log file,
  • with overwriting all files already existing,
  • with assuming yes for all queries like on an error.

WinRAR automatically extracts all volumes of a multi-volume archive.

WinRAR exits with a value greater or equal 1 on an error as documented in help of WinRAR on help page List of WinRAR exit codes.

The archive file name is assigned to environment variable ArchiveFile in case of a not 100% successful extraction of an archive file indicated by exit of WinRAR with value 0 and next an error message line is output writing exit code of WinRAR and the file name into the error log file. The error log file is encoded depending on character encoding and code page defined by Windows command processor on starting batch file processing.

The environment variables ErrorLevel and ArchiveFile are referenced with two percent signs on each side because of Windows command processor replaces already on parsing entire command block before executing outer FOR all %% by just %. The command CALL results in a second parsing of the ECHO command line before executing ECHO which results in replacing %Errorlevel% by current value of this environment variable as well as %ArchiveFile% by current archive file name.

An ECHO line like echo Error %ErrorLevel% on extracting "%ArchiveFile%" without command CALL would result in replacing %ErrorLevel% by current value of environment variable ErrorLevel before outer FOR is executed at all which means with 0 and would replace %ArchiveFile% by an empty string which of course would not be helpful.

The archive file name is assigned to environment variable ArchiveFile and referenced like ErrorLevel to handle also a file name like Archive%20!Important!.rar correct.

On an error on a multi-volume archive none of the RAR archive parts are deleted resulting in the approach of extracting all volumes of the multi-volume archive multiple times with each part of multi-volume archive file written into the error log file.

The archive file is deleted on a successful extraction of a single archive file respectively all parts of a multi-volume archive are deleted on successful extraction of a multi-volume archive. Additionally an environment variable is set with # plus subdirectory name as environment variable name and full path of subdirectory as value to remember which subdirectories contained at least one successfully extracted archive to move it later. It is required for this simple method that no subdirectory name contains an equal sign.

The above code could not work on FAT32 or ExFAT drives on more than one *.rar or *.zip file in a subdirectory. In this case it is necessary to use command DIR executed by FOR in a separate command process started with %ComSpec% /C in background and capture the output archive file names. Then the inner FOR runs with a list of archive file names not modified during the loop iterations as caused by deletion of the archive files on FAT32 and ExFAT drives.

This alternate batch file is also needed if an archive file contains itself *.rar or *.zip files which should not be extracted by chance as it can happen with the batch file code above.

So this second batch code is more safe than the first one.

@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

Note: WinRAR version 5.70 does not support writing the file names of extracted files from ZIP archives to the extraction log file. This is documented at top of help page Switch -LOG[fmt][=name] - write names to log file.

Finally, if any archive was successfully extracted and then deleted, the batch file moves all folders for which an environment variable starting with # exists into subdirectory done in parent source directory. So subdirectories with no successful extraction of at least one archive are ignored on folder movement as well as subdirectories which have no archive file. The code could be easier if the final destination directory is not a subdirectory of the parent source directory.

One more variant which moves a folder immediately after archive extraction(s) finished. It is necessary in this case to work with a captured list of subdirectory names as the list of subdirectories changes during loop iterations of outer FOR loop. FINDSTR is used to filter out folder done from list of subdirectory names.

This batch file waits for a user choice to break execution with choosing no automatically after two seconds. So the batch job can be safely broken by a user. This prompt can be avoided by starting the batch file with parameter /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

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • del /?
  • dir /?
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • if /?
  • md /?
  • move /?
  • set /?
  • setlocal /?
查看更多
登录 后发表回答