Как создать подпапку при распаковке RAR/ZIP, если в архиве ее нет?

У меня есть много архивов RAR или ZIP, которые нужно распаковать. Некоторые архивы содержат одну папку со всеми файлами в этой папке. В некоторых других архивах все файлы находятся на корневом уровне.

Дело 01

Archive01.rar
    MyFolder
       file01.txt
       file02.txt 
       file03.txt 
       etc.

Дело 02

Archive02.rar
    -file01.txt
    -file02.txt
    -file031.txt
    etc.

Я знаю, как распаковать все архивы в подпапку.

Но как создать подпапку только тогда, когда в архиве ее нет?

Я имею в виду, что в пакетном процессе для обработки тысяч архивов не должно быть дополнительной папки, созданной при извлечении, если файл архива принадлежит case 01 . Но если файл архива относится к кейсу 02, извлечение должно производиться в подпапку с именем файла архива.

Результат варианта 01

 MyFolder <- Folder
   file01.txt
   file02.txt 
   file03.txt 
   etc.

Результат варианта 02

 Archive02 <-Folder base on the archive name
   -file01.txt
   -file02.txt
   -file031.txt
   etc.

person Nexus Fred    schedule 22.04.2017    source источник
comment
Что вы имеете в виду, но не как создать его только тогда, когда его нет?   -  person Pablo Escobar    schedule 22.04.2017
comment
Если в архиве дело 01, я не создаю папку, если дело 02, я создаю папку для извлечения всех файлов.   -  person Nexus Fred    schedule 22.04.2017


Ответы (2)


Консольная версия Rar.exe имеет команду l для перечисления содержимого файла архива в соответствии с текстовым файлом Rar.txt в папке программных файлов WinRAR, который является руководством для консольной версии. Список, выводимый при запуске Rar.exe с помощью команды l (или L), может быть обработан в пакетном файле, чтобы определить, содержит ли файл архива RAR на верхнем уровне только один каталог и ничего больше. Но Rar.exe поддерживает как бесплатные UnRAR.exe только архивы RAR.

Чтобы также поддерживать ZIP-архивы, необходимо использовать GUI версии WinRAR.exe, которая поддерживает извлечение архивов RAR и ZIP и некоторых других типов архивов.

Руководство для WinRAR.exe представляет собой справку по WinRAR, которую можно открыть, щелкнув в меню Справка пункт меню Темы справки при запуске WinRAR. . На вкладке справки Содержание есть элемент списка Режим командной строки со всей необходимой информацией на соответствующих страницах справки для запуска WinRAR.exe из командной строки.

Из списка Команд видно, что WinRAR.exe не поддерживает команду l для вывода содержимого файла архива в окно консоли, поскольку является приложением с графическим интерфейсом пользователя.

Таким образом, на самом деле невозможно определить из командной строки или в пакетном файле, содержит ли архивный файл на верхнем уровне только один каталог при использовании WinRAR.exe.

Однако на самом деле это не имеет значения, так как было бы неэффективно сначала анализировать файл архива на наличие имен файлов и каталогов, а затем использовать соответствующую команду для извлечения файла архива без указания или с указанием дополнительной папки для извлечения в командной строке.

Гораздо эффективнее сначала извлечь все *.rar (а позже и все *.zip) файлы, используя всего один вызов WinRAR с переключателем -ad для извлечения каждого файла архива в подкаталог с именем архива. файл и второй исключают необходимость в каждом каталоге извлечения, поскольку соответствующий архивный файл содержит только один каталог на верхнем уровне.

Этот разумный подход используется в приведенном ниже пакетном файле, который содержит следующие дополнительные функции, которые, надеюсь, сделают его полезным для многих пользователей WinRAR:

  1. Рабочий каталог может быть указан в качестве первого аргумента при вызове пакетного файла, который может быть даже путем UNC.

  2. Пакетный файл автоматически определяет, где установлен WinRAR.exe, и работает также для тех случаев использования, когда 32-разрядная или 64-разрядная версия WinRAR не установлена ​​в каталог программных файлов по умолчанию (как на всех моих компьютерах).

Примечание. Пакетный файл с комментариями, опубликованный ниже, не проверяет, был ли ранее извлечен существующий архивный файл в текущем или указанном каталоге. Поэтому не рекомендуется запускать пакетный файл несколько раз в каталоге с архивными файлами, которые после обработки не удаляются из этого каталога.

@echo off
rem Change working directory if batch file was started with an argument.
if not "%~1" == "" (
    pushd "%~1" 2>nul
    if errorlevel 1 (
        echo Specified directory "%~1" does not exist.
        echo/
        pause
        goto :EOF
    )
)

setlocal EnableExtensions DisableDelayedExpansion

rem Does WinRAR exist in default program files folder?
set "WinRAR=%ProgramFiles%\WinRAR\WinRAR.exe"
if exist "%WinRAR%" goto StartExtraction

rem Does WinRAR exist in default program files folder for x86 applications?
set "WinRAR=%ProgramFiles(x86%\WinRAR\WinRAR.exe"
if exist "%WinRAR%" goto StartExtraction

rem Try to determine installation location of WinRAR.exe from registry.
set "TypeToken=2"
goto GetPathFromRegistry

rem On Windows Vista and later REG.EXE outputs without version info:

rem HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths\WinRAR.exe
rem    (Default)    REG_SZ    Full path to WinRAR\WinRAR.exe

rem There are only spaces used to separate value name, value type and value string.

rem But REG.EXE version 3.0 outputs on Windows XP with version info:

rem ! REG.EXE VERSION 3.0
rem
rem HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths\WinRAR.exe
rem     <NO NAME>   REG_SZ  Full path to WinRAR\WinRAR.exe

rem NOTE: There are 4 indent spaces and 2 separating tabs in REG 3.0 output line.

rem So either token 2 or token 3 contains value type REG_SZ
rem used to identify the line with the wanted information.

:GetPathFromRegistry
for /F "skip=1 tokens=%TypeToken%*" %%A in ('%SystemRoot%\System32\reg.exe QUERY "HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths\WinRAR.exe" /ve 2^>nul') do (
    if "%%A" == "REG_SZ" (
        if exist "%%~fB" (
            set "WinRAR=%%~fB"
            goto StartExtraction
        )
    ) else if "%%A" == "NAME>" (
        set "TypeToken=3"
        goto GetPathFromRegistry
    )
)

endlocal
if not "%~1" == "" popd
echo Could not determine directory containing WinRAR.exe.
echo/
echo Please configure it manually in file: %~f0
echo/
pause
goto :EOF


rem WinRAR supports multiple archive types on extraction.
rem Specify here the archive file extensions for extraction.

:StartExtraction
for %%I in (rar zip) do call :ExtractArchives %%I

rem Restore previous command environment, restore previous current directory
rem and exit this batch file without fall through to the subroutines below.
endlocal
if not "%~1" == "" popd
goto :EOF


rem The subroutine ExtractArchives processes all archive files in current
rem directory with the file extension passed to subroutine as first argument.

rem WinRAR is called once to extract all files with specified file extension
rem for extraction into a subdirectory with name of the archive file.

rem Then one more subroutine is called for each archive file to determine
rem if it is safe to move the extracted archive file contents up one level.

:ExtractArchives
if not exist "*.%~1" goto :EOF
"%WinRAR%" x -ad -cfg- -ibck -y -- "*.%~1"
for %%A in ("*.%~1") do call :MoveUpExtracted "%%~nA"
goto :EOF

rem The subroutine MoveUpExtracted first checks if for the archive file
rem passed to the subroutine as first argument a subdirectory exists at
rem all, i.e. the extraction before was successful for that archive.

rem Next it counts the subdirectories in the archive extraction directory.
rem Nothing is moved up if there is more than 1 subdirectory in archive
rem extraction directory.

rem Also nothing is moved up if archive extraction directory contains
rem 1 or more files.

rem After verification of archive extraction directory really containing
rem only a single subdirectory and nothing else, the name of the archive
rem extraction directory is compared case-insensitive with the name of
rem the single subdirectory in archive extraction directory. On equal
rem directory names the archive extraction directory is renamed by
rem appending _tmp to make it possible to move the subdirectory with same
rem name up one level in directory hierarchy. There is hopefully by chance
rem never a directory present in current directory with name of an archive
rem file and _tmp appended.

rem Next it is checked if in current directory there is not already existing
rem a directory with name of the subdirectory from extracted archive in which
rem case it is also not possible to move the directory up one level. In this
rem special use case the archive extraction directory is kept containing just
rem a single subdirectory with restoring original directory name.

rem Then the single subdirectory in archive extraction directory is moved up
rem one level which is very fast as just the file allocation table is updated
rem and no data is really moved.

rem The directory movement could fail if the extracted directory has hidden
rem attribute set. In this case temporarily remove the hidden attribute,
rem move the directory up one level in directory hierarchy and set the
rem hidden attribute again on the directory.

rem On a succesful moving up of the extracted directory the (renamed)
rem extraction directory being now empty is deleted as not further needed.


:MoveUpExtracted
if not exist "%~1\" (
    echo Error: No folder for archive %~1
    goto :EOF
)

echo Processing archive folder "%~1"
set FolderCount=0
set "FolderName="
for /F "delims=" %%D in ('dir "%~1\*" /AD /B 2^>nul') do (
    if defined FolderName goto :EOF
    set /A FolderCount+=1
    set "FolderName=%%D"
)
if not %FolderCount% == 1 goto :EOF

for /F "delims=" %%F in ('dir "%~1\*" /A-D /B 2^>nul') do goto :EOF

set "ParentRenamed=0"
set "ParentFolder=%~1"
if /I "%~1" == "%FolderName%" (
    ren "%~1" "%~1_tmp" 2>nul
    if errorlevel 1 (
        echo Failed to rename "%~1" to "%~1_tmp".
        goto :EOF
    )
    set "ParentFolder=%~1_tmp"
    set "ParentRenamed=1"
)

if exist "%FolderName%" (
    if %ParentRenamed% == 1 ren "%~1_tmp" "%~1"
    echo Error: Folder exists "%FolderName%"
    goto :EOF
)

move "%ParentFolder%\%FolderName%" "%FolderName%" >nul 2>nul
if not errorlevel 1 (
    rd "%ParentFolder%"
    goto :EOF
)

%SystemRoot%\System32\attrib.exe -h "%ParentFolder%\%FolderName%" >nul
move "%ParentFolder%\%FolderName%" "%FolderName%" >nul
if errorlevel 1 (
    if %ParentRenamed% == 1 (
        ren "%ParentFolder%" "%~1"
        goto :EOF
    )
)

%SystemRoot%\System32\attrib.exe +h "%FolderName%"
rd "%ParentFolder%"
goto :EOF

Я использую 32-битную Windows, начиная с Windows 95, но я никогда не сталкивался с ограничением MAX_PATH, т.е. абсолютные имена файлов/папок длиннее 259 символов.

Таким образом, было действительно интересно и очень трудоемко переписать пакетный файл, чтобы он работал даже с очень длинными именами архивных файлов, например, ровно 256 символов для имени файла + расширение файла.

Во время разработки командного файла ниже я обнаружил следующее:

  1. Некоторые команды, такие как DIR, FOR, RD и REN, поддерживают короткие имена 8.3 в пути И имени файла/папки, а другие команды, такие как ATTRIB и MOVE, поддерживают их только в пути, но не в имени файла/папки (по крайней мере, в Windows XP).
    Поэтому невозможно переместить папку или изменить ее атрибуты, используя ее короткое имя 8.3.

  2. Все команды не работают при использовании только относительных имен папок с относительным путем к папке, когда имя папки с полным путем длиннее 259 символов. Это означает, что интерпретатор команд Windows сначала определяет имя папки с полным путем перед выполнением любой команды. Таким образом, текущий каталог должен иметь короткий путь при обработке архивов с очень длинными именами или содержащих каталог с очень длинным именем.

  3. Я не мог понять, как получить короткое имя папки или ее путь, используя %~fs1, как объяснено call /? или %%~fsI (в пакетном файле), как объяснено for /?, когда интерпретатор команд Windows анализирует только относительный путь к папке, т.е. только длинный имя папки без пути.

  4. При запуске команды DIR с опцией /X для получения короткого имени каталога третий столбец содержит короткое имя, а четвертый столбец — длинное имя. Но короткое имя в третьем столбце может отсутствовать в очень коротких именах папок.

Вывод dir /AD /X в английской версии Windows 7 SP1 x64, выполненной на разделе NTFS с установленным немецким языком в настройках региона и языка Windows:

 Volume in drive C is System
 Volume Serial Number is 7582-4210

 Directory of C:\Temp\Test

29.04.2017  22:39    <DIR>                       .
29.04.2017  22:39    <DIR>                       ..
29.04.2017  22:39    <DIR>          ARCHIV~1     archive_with_a_very_very_very_..._long_name_1
29.04.2017  22:39    <DIR>                       Batch
29.04.2017  22:39    <DIR>                       xyz

Та же команда dir /AD /X выполняется в немецкой Windows XP SP3 x86 на разделе FAT32, также с установленным немецким языком в настройках региона и языка Windows:

 Datenträger in Laufwerk F: ist TEMP
 Volumeseriennummer: CAA5-41AA

 Verzeichnis von F:\Temp

29.04.2017  22:39    <DIR>                       .
29.04.2017  22:39    <DIR>                       ..
29.04.2017  22:39    <DIR>          BATCH        Batch
29.04.2017  22:39    <DIR>                       xxx
29.04.2017  22:39    <DIR>          ARCHIV~1     archive_with_a_very_very_very_..._long_name_1

Примечание. Очень длинное имя каталога было усечено мной до ... в названии.

Почему каталог Batch имеет на компьютере с Windows XP короткое имя BATCH, а не короткое имя на Windows 7, для меня не очень понятно.

Вот пакетный скрипт, поддерживающий также длинные имена архивов и длинные имена каталогов в архиве, если путь к текущему каталогу короткий.

@echo off
rem Change working directory if batch file was started with an argument.
if not "%~1" == "" (
    pushd "%~1" 2>nul
    if errorlevel 1 (
        echo Specified directory "%~1" does not exist.
        echo/
        pause
        goto :EOF
    )
)

setlocal EnableExtensions DisableDelayedExpansion

rem Does WinRAR exist in default program files folder?
set "WinRAR=%ProgramFiles%\WinRAR\WinRAR.exe"
if exist "%WinRAR%" goto StartExtraction

rem Does WinRAR exist in default program files folder for x86 applications?
set "WinRAR=%ProgramFiles(x86%\WinRAR\WinRAR.exe"
if exist "%WinRAR%" goto StartExtraction

rem Try to determine installation location of WinRAR.exe from registry.
set "TypeToken=2"
goto GetPathFromRegistry

rem On Windows Vista and later REG.EXE outputs without version info:

rem HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths\WinRAR.exe
rem    (Default)    REG_SZ    Full path to WinRAR\WinRAR.exe

rem There are only spaces used to separate value name, value type and value string.

rem But REG.EXE version 3.0 outputs on Windows XP with version info:

rem ! REG.EXE VERSION 3.0
rem
rem HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths\WinRAR.exe
rem     <NO NAME>   REG_SZ  Full path to WinRAR\WinRAR.exe

rem NOTE: There are 4 indent spaces and 2 separating tabs in REG 3.0 output line.

rem So either token 2 or token 3 contains value type REG_SZ
rem used to identify the line with the wanted information.

:GetPathFromRegistry
for /F "skip=1 tokens=%TypeToken%*" %%A in ('%SystemRoot%\System32\reg.exe QUERY "HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths\WinRAR.exe" /ve 2^>nul') do (
    if "%%A" == "REG_SZ" (
        if exist "%%~fB" (
            set "WinRAR=%%~fB"
            goto StartExtraction
        )
    ) else if "%%A" == "NAME>" (
        set "TypeToken=3"
        goto GetPathFromRegistry
    )
)

endlocal
if not "%~1" == "" popd
echo Could not determine directory containing WinRAR.exe.
echo/
echo Please configure it manually in file: %~f0
echo/
pause
goto :EOF


rem WinRAR supports multiple archive types on extraction.
rem Specify here the archive file extensions for extraction.

rem But first delete temporary folder from a previous breaked execution.

:StartExtraction
rd /Q /S # 2>nul

for %%I in (rar zip) do call :ExtractArchives %%I

rem Restore previous command environment, restore previous current directory
rem and exit this batch file without fall through to the subroutines below.
endlocal
if not "%~1" == "" popd
goto :EOF


rem The subroutine ExtractArchives processes all archive files in current
rem directory with the file extension passed to subroutine as first argument.

rem WinRAR is called once to extract all files with specified file extension
rem for extraction into a subdirectory with name of the archive file.

rem Then one more subroutine is called for each archive file to determine
rem if it is safe to move the extracted archive file contents up one level.

:ExtractArchives
if not exist "*.%~1" goto :EOF
"%WinRAR%" x -ad -cfg- -ibck -y -- "*.%~1"
for %%A in ("*.%~1") do call :MoveUpExtracted "%%~nA" %1
goto :EOF

rem The subroutine MoveUpExtracted first checks if for the archive file
rem passed to the subroutine as first argument a subdirectory exists at
rem all, i.e. the extraction before was successful for that archive, and
rem determines short 8.3 name of this directory.

rem Next it counts the subdirectories in the archive extraction directory
rem using short directory name. Nothing is moved up if there is more than
rem 1 subdirectory in archive extraction directory.

rem Also nothing is moved up if archive extraction directory contains
rem 1 or more files.

rem After verification of archive extraction directory really containing
rem only a single subdirectory and nothing else, the current archive folder
rem is renamed to # (single character folder name) using short folder name.

rem This folder rename should work in general. The current archive folder
rem is kept in case of this folder rename fails unexpected because it is
rem not yet known if the current directory does not already contain the
rem single directory extracted from current archive or rename failed
rem because of a permission or a directory sharing access restriction.

rem Next it is checked if in current directory there is not already existing
rem a directory with name of the subdirectory from extracted archive in which
rem case it is also not possible to move the directory up one level. In this
rem special use case the archive extraction directory is kept containing just
rem a single subdirectory with restoring original directory name. In case of
rem restoring archive directory fails unexpected, the directory with name #
rem is deleted and the archive is extracted once again into a directory with
rem name of archive file.

rem It is clear on this point that the single directory in archive extraction
rem directory can be moved up to current directory from directory wit having
rem now the temporary name #.

rem Moving a directory with command MOVE is not possible if hidden attribute
rem is set on directory. For that reason it is checked next if the directory
rem to move up has hidden attribute set using its short directory name.

rem In case of directory has hidden attribute is indeed set, it is removed
rem which is also verified. The verification can't be done with errorlevel
rem evaluation as external command ATTRIB does not set errorlevel on failed
rem attribute change. So the attribute check is done once again after the
rem hidden attribute is removed with ATTRIB.

rem ATTRIB also fails to change the attribute if absolute folder path is
rem longer than 259 characters. In this case the current extraction folder
rem with temporary name # is deleted completely and the current archive is
rem extracted once again to current directory without creation of an
rem additional directory with name of archive file.

rem Then the single subdirectory in archive extraction directory having
rem now name # is also renamed to # using short directory name to avoid
rem a problem on next command MOVE with an absolute folder path longer
rem than 259 characters as much as possible.

rem The directory extracted from archive with name # in directory # is
rem moved up to current directory with suppressing all errors which could
rem occur for example if path of current directory plus name of directory
rem as extracted from archive file is too long.

rem The directory # in current directory with its subdirectory # is deleted
rem on a moving error and the current archive file is extracted once again
rem into current directory without creation of an additional directory with
rem name of archive file.

rem But on successful movement of the folder with correct name to current
rem directory the hidden attribute is set on folder if the extracted folder
rem has it also set before moving the folder and the finally empty folder #
rem is deleted before exiting subroutine.


:MoveUpExtracted
set "FolderToCheck=%~f1"
set "FolderToCheck=%FolderToCheck:~0,258%"
for /F "skip=5 tokens=4*" %%X in ('dir "%FolderToCheck%*" /AD /X 2^>nul') do (
    if "%%Y" == "%~1" set "ArchiveFolder=%%X" & goto Subfolders
    if "%%Y" == "" if /I "%%X" == "%~1" set "ArchiveFolder=%%X" & goto Subfolders
)
echo Error: No folder for archive %~1
goto :EOF

:Subfolders
@echo off
echo Processing archive folder "%~1"
set FolderCount=0
set "FolderName="
for /F "delims=" %%D in ('dir "%ArchiveFolder%\*" /AD /B 2^>nul') do (
    if defined FolderName goto :EOF
    set /A FolderCount+=1
    set "FolderName=%%D"
)
if not %FolderCount% == 1 goto :EOF

for /F "delims=" %%F in ('dir "%ArchiveFolder%\*" /A-D /B 2^>nul') do goto :EOF

ren "%ArchiveFolder%" # 2>nul
if errorlevel 1 (
    echo Error: Failed to rename "%~1"
    goto :EOF
)

set "FolderToCheck=%~dp1%FolderName%"
set "FolderToCheck=%FolderToCheck:~0,258%"
for /F "skip=5 tokens=4*" %%X in ('dir "%FolderToCheck%*" /AD /X 2^>nul') do (
    if "%%Y" == "%FolderName%" goto FolderExist
    if "%%Y" == "" if /I "%%X" == "%FolderName%" goto FolderExist
)

set "HiddenFolder=0"
set "FolderToCheck=%~dp1#\%FolderName%"
set "FolderToCheck=%FolderToCheck:~0,258%"
for /F "skip=5 tokens=4*" %%X in ('dir "%FolderToCheck%*" /AD /X 2^>nul') do (
    if "%%Y" == "%FolderName%" set "FolderToMove=%%X" & goto CheckHidden
    if "%%Y" == "" if /I "%%X" == "%FolderName%" set "FolderToMove=%%X" & goto CheckHidden
)

:CheckHidden
for %%X in ("#\%FolderToMove%") do (
    for /F "tokens=2 delims=h" %%H in ("%%~aX") do (
        if %HiddenFolder% == 1 goto ArchiveExtract
        set "HiddenFolder=1"
        %SystemRoot%\System32\attrib.exe -h "#\%FolderName%"
        goto CheckHidden
    )
)

ren "#\%FolderToMove%" # 2>nul
move #\# "%FolderName%" >nul 2>nul
if errorlevel 1 goto ArchiveExtract

if %HiddenFolder% == 1 %SystemRoot%\System32\attrib.exe +h "%FolderName%"
rd #
goto :EOF

:ArchiveExtract
rd /Q /S #
"%WinRAR%" x -cfg- -ibck -y -- "%~1.%~2"
goto :EOF

:FolderExist
echo Error: Folder exists "%FolderName%"
ren # "%~1" 2>nul
if not errorlevel 1 goto :EOF
rd /Q /S #
"%WinRAR%" x -ad -cfg- -ibck -y -- "%~1.%~2"
goto :EOF

Определенно было бы лучше написать консольное приложение на C, C++ или C# с учетом длинного пути, заменив подпрограмму MoveUpExtracted в приведенных выше пакетных сценариях.

В Windows 10 версии 1607 (юбилейное обновление) или более поздних версиях Windows ограничение MAX_PATH в 260 символов (259 символов плюс завершающий нулевой байт) можно отключить с помощью групповой политики или путем добавления значения реестра, см.

Чтобы понять, какие команды используются и как они работают, откройте окно командной строки, выполните в нем следующие команды и внимательно прочитайте все страницы справки, отображаемые для каждой команды.

  • attrib /?
  • call /?
  • dir /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • move /?
  • pause /?
  • popd /?
  • pushd /?
  • rd /?
  • reg /?
  • reg query /?
  • rem /?
  • ren /?
  • set /?
  • setlocal /?

Читайте также статьи Microsoft:

person Mofi    schedule 23.04.2017
comment
Большое спасибо !! Это работает именно так, как я мечтаю :*) Вы делаете мой день! - person Nexus Fred; 23.04.2017

Вам нужно будет проверить, находятся ли данные внутри папки или в корне.

Если он находится в корне, нажмите extract to /foldername (Case 02)

в противном случае просто извлеките сюда (Case 01)

введите здесь описание изображения

person Pablo Escobar    schedule 22.04.2017
comment
Спасибо, но я не могу сделать это вручную с 15000 архивами :*) Мне нужно пакетное решение! - person Nexus Fred; 22.04.2017