如何在RAR/ZIP提取中创建子文件夹,如果归档文件没有?

如何在RAR/ZIP提取中创建子文件夹,如果归档文件没有?

问题描述:

我有很多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. 

我知道如何提取所有档案到子文件夹。

但是,如何仅在档案中不存在时创建子文件夹?

我的意思是在处理数千个档案的批处理过程中,如果归档文件属于案例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. 
+0

你是什么意思“的意思,但不是如何创建它,只有当不存在?“ –

+0

如果档案是“案例01”,我不创建文件夹,如果“案例02”我创建了一个文件夹来提取所有文件。 –

的控制台版本Rar.exe有命令l列表归档文件根据文本文件Rar.txt的内容在的程序文件文件夹中WinRAR是控制台版本的手册。使用命令l(或L)运行Rar.exe的列表输出可以在批处理文件中进行处理,以确定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安装工作也不被安装在默认的Program Files目录中的用例与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. DIRFORRDREN支持短8.3路径名称和一些命令文件/文件夹名称,而其他命令如ATTRIBMOVE仅在路径中支持它们,但不支持文件/文件夹名称(至少在Windows XP上)。
    所以无法使用其简短的8.3名称来移动文件夹或更改其属性。

  2. 当包含完整路径的文件夹名称长于259个字符时,所有命令都无法使用相对文件夹路径使用相对文件夹名称。这意味着Windows命令解释器在执行任何命令之前首先使用完整路径确定文件夹名称。所以当前目录应该有一个很短的路径来处理具有很长名称或包含名称很长的目录的档案。

  3. 我无法弄清楚如何获得一个文件夹的短名称或使用由for /?解释的时候只是一个相对的文件夹路径是由Windows命令解释器解析%~fs1(批处理文件)由call /?%%~fsI解释其路径,即只是没有路径的文件夹的长名称。

  4. 在运行命令DIR与选项/X获取目录的短名称,第三列包含短名称和第四列的长名称。但是第三列中的短名称可能会在很短的文件夹名称中丢失。

    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执行:的dir /AD /X英文版Windows 7 SP1 64位

输出在NTFS分区上与德国执行在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 

注:FAT32分区还与德国在Windows区域和语言设置来设置这个非常长的目录名在我的名字中被截断了...

为什么目录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 /?

阅读也是微软的文章:

+0

非常感谢!它的工作原理与我梦想的一样:*)你让我的一天! –

你必须检查,如果数据是内部文件夹或根。

如果它的根点击extract to /foldername(案例02)

否则只提取这里(案例01)

enter image description here

+1

谢谢,但我不能用15000个档案来做到这一点:*)我需要一个批处理解决方案! –