Search code examples
windowsbatch-fileziprarwinrar

Batch does not work correctly if the folder name contains Unicode characters


Previously, I was asked to create the following batch.

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

I thought it was working properly, but when the folder name contained Unicode characters(❤, ㉓ etc), the compressed file in the folder was never decompressed. Therefore, how can I extract Unicode characters even if they are included in the folder name?

As another problem, the following batch does not move to the done folder if there are no compressed files (zip, rar, partX.rar) in the folder. Even if there are no compressed files in the folder, I want to move to the done folder each time.

Before:

C:
│
└─test
    ├─AAAA
    │      XXXX.rar
    │      XXXX.jpg
    │
    ├─㉓
    │      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
    │  │
    │  ├─㉓
    │  │      XXXX.doc
    │  │      XXXX.jpg
    │  │
    │  ├─DDDD
    │  │      XXXX.doc
    │  │      XXXX.jpg
    │  │
    │  └─EEEE
    │         XXXX.jpg
    │
    └─CCCC(error_file)
           XXXX.rar
           XXXX.jpg

@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

Solution

  • On Windows 8 or a newer Windows version with Consolas set by default as font for console windows the following batch file should work:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    
    for /F "tokens=2 delims=:" %%I in ('%SystemRoot%\System32\chcp.com') do set "CodePage=%%I"
    %SystemRoot%\System32\chcp.com 65001 2>nul
    
    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 "NoFolderMove="
        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 "NoFolderMove=1"
                    set "ArchiveFile=%SourceFolder%\%%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 "%SourceFolder%\%%I\%%J" ) else for %%# in ("%%~nJ") do del /F /Q "%SourceFolder%\%%I\%%~n#.part*%%~xJ"
                )
            )
        )
        if not defined NoFolderMove (
            md "%SourceFolder%\done" 2>nul
            if exist "%SourceFolder%\done\" move /Y "%SourceFolder%\%%I" "%SourceFolder%\done\" >nul
            %PromptForBreak% %SystemRoot%\System32\choice.exe /C NY /N /T 2 /D N /M "Break execution [N/Y]? "
            %PromptForBreak% if errorlevel 2 goto EndBatch
        )
    )
    
    :EndBatch
    if defined CodePage %SystemRoot%\System32\chcp.com %CodePage%
    endlocal
    

    This batch file makes the opposite as previously asked regarding to folder movement. All folders are moved to subfolder done, not only those in which an archive file was extracted successfully. Subfolders with an error on archive file extraction remain in source folder like in previous version.

    On Windows 7 is configured by default Raster Fonts which is Terminal which does not support UTF-8. This results in getting displayed during execution of the batch file:

    The system cannot write to the specified device.

    The reason was analyzed once by eryksun and can be read in the comments below answer on Using another language (code page) in a batch file made for others.

    The error message can be avoided by removing from batch file the line:

    echo Extracting "%SourceFolder%\%%I\%%J" ...
    

    The archive file extraction task and folder movement is done nevertheless by the batch file on Windows 7 even on output of this error message by cmd.exe.

    On Windows XP is configured by default also Raster Fonts which is also Terminal not supporting UTF-8. But On Windows XP the Windows command processor stops execution of the batch file after fourth line. So this batch file cannot be used on Windows XP, at least not without changing the font in properties of process running this batch file.

    A workaround for Windows XP would be using short 8.3 folder names which consist of only ASCII characters as done by this batch file.

    @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=| tokens=4" %%I in ('dir "%SourceFolder%\*" /AD-H /ON /X 2^>nul ^| %SystemRoot%\System32\findstr.exe /L /C:"<DIR>" ^| %SystemRoot%\System32\findstr /I /E /L /V /C:" done" /C:" ." /C:" .."') do (
        set "NoFolderMove="
        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 "NoFolderMove=1"
                    set "ArchiveFile=%SourceFolder%\%%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 "%SourceFolder%\%%I\%%J" ) else for %%# in ("%%~nJ") do del /F /Q "%SourceFolder%\%%I\%%~n#.part*%%~xJ"
                )
            )
        )
        if not defined NoFolderMove (
            md "%SourceFolder%\done" 2>nul
            if exist "%SourceFolder%\done\" move /Y "%SourceFolder%\%%I" "%SourceFolder%\done\" >nul
            %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
    

    The disadvantage is that the moved directories have in subdirectory done finally the short 8.3 directory names and not the original directory names.