【问题标题】:How can I compress (/ zip ) and uncompress (/ unzip ) files and folders with batch file without using any external tools?如何在不使用任何外部工具的情况下使用批处理文件压缩 (/ zip ) 和解压缩 (/ unzip ) 文件和文件夹?
【发布时间】:2015-03-18 13:51:52
【问题描述】:

我知道这里有很多类似的问题,但我对答案(甚至是问题)并不完全满意。

主要目标是兼容性 - 它应该适用于尽可能广泛的 Windows 机器(包括 XPVistaServer 2003 - 它们仍然占据了大约 20% 的 Windows 份额)并且生成的文件应该可在 Unix/Mac 机器上使用(因此最好使用标准存档/压缩格式)。

选项是什么:

  1. 创建一个执行某些 zip 算法的批处理。显然这是可能的 - 但仅适用于单个文件并使用 CERTUTIL 进行二进制处理(某些机器默认没有 CERTUTIL,并且无法安装在 Windows XP Home Edition 上)
  2. 使用shell.applicationWSH。在我看来,这是最好的选择。它允许压缩整个目录,并且可以在每台 Windows 机器上使用
  3. Makecab - 尽管它的压缩不是那么便携,但它在每台 Windows 机器上都可用。一些外部程序(如 7-Zip)能够提取 .CAB 内容,但当文件需要在 Unix/Mac 上使用时就不太方便了。虽然压缩单个文件非常简单,但保留目录结构需要更多的努力。
  4. 使用 .NET Framework - 不是很好的选择。从 .NET 2.0 开始有 GZipStream,但它只允许压缩单个文件。 .NET 4.5 具有 Zip 功能,但在 Vista 和 XP 上不受支持。甚至更多 - XP 和 Server 2003 上默认不安装 .NET,但由于极有可能拥有 .NET 2.0 到 4.0,这是一个相当大的选择。
  5. PowerShell - 由于它依赖于 .NET,因此具有相同的功能。 XP、Server 2003 和 Vista 上默认没有安装它,所以我会跳过它。

【问题讨论】:

  • 你的Shell.Application方法有2GB size limitation吗?另请注意,ZIP64 格式仅支持 Vista or newer 中的 Shell.Application
  • @DavidRuhmann - 我测试过的最大文件是 1.5gb 。很可能它的大小是有限的。将对其进行测试并更新。
  • 我刚刚测试了my Shell.Application solution,成功压缩了包含我的 XP Mode 虚拟硬盘的目录,总共大约 6.2 gig。它压缩到 2.44 演出。然后我使用 IZArc 解压缩,所有内容都提取出来,并且大小与原件相同。这是在 Win7 x64 上。
  • @rojo @ DavidRuhmann - 在 win8.1x64 上,zip 文件的限制似乎在 8gb 左右
  • 所有当前支持的 Microsoft Windows 平台都安装了 PowerShell。 Compress-ArchiveExpand-Archive

标签: .net zip jscript batch-file


【解决方案1】:

以下是答案:

1。使用“纯”批处理脚本压缩/解压缩文件。

这要归功于 Frank Westlake 的 ZIP.CMDUNZIP.CMD(需要管理员权限并需要 FSUTILCERTUTIL)。对于 Win2003 和 WinXP,它将需要 2003 Admin Tool Pack,它将安装 CERTUTIL。小心 ZIP.CMD 语法落后:

ZIP.CMD destination.zip source.file

而且它只能压缩单个文件。

2。使用 Shell.Application

我花了一些时间创建一个 jscript/batch 混合脚本,用于压缩/解压缩文件和目录(以及更多功能)的常见用途。Here's a link to it(它变得太大,无法在答案中发布)。 可以直接使用其.bat 扩展名,并且不会创建任何临时文件。 我希望帮助消息能够描述如何使用它。

一些例子:

// unzip content of a zip to given folder.content of the zip will be not preserved (-keep no).Destination will be not overwritten (-force no)
call zipjs.bat unzip -source C:\myDir\myZip.zip -destination C:\MyDir -keep no -force no

// lists content of a zip file and full paths will be printed (-flat yes)
call zipjs.bat list -source C:\myZip.zip\inZipDir -flat yes

// lists content of a zip file and the content will be list as a tree (-flat no)
call zipjs.bat list -source C:\myZip.zip -flat no

// prints uncompressed size in bytes
zipjs.bat getSize -source C:\myZip.zip

// zips content of folder without the folder itself
call zipjs.bat zipDirItems -source C:\myDir\ -destination C:\MyZip.zip -keep yes -force no

// zips file or a folder (with the folder itslelf)
call zipjs.bat zipItem -source C:\myDir\myFile.txt -destination C:\MyZip.zip -keep yes -force no

// unzips only part of the zip with given path inside
call zipjs.bat unZipItem -source C:\myDir\myZip.zip\InzipDir\InzipFile -destination C:\OtherDir -keep no -force yes
call zipjs.bat unZipItem -source C:\myDir\myZip.zip\InzipDir -destination C:\OtherDir 

// adds content to a zip file
call zipjs.bat addToZip -source C:\some_file -destination C:\myDir\myZip.zip\InzipDir -keep no
call zipjs.bat addToZip -source  C:\some_file -destination C:\myDir\myZip.zip

压缩过程中的一些已知问题:

  • 如果系统驱动器(通常是 C:)上没有足够的空间,脚本可能会产生各种错误,大多数情况下脚本会停止。这是由于 Shell.Application 积极使用 % TEMP% 文件夹来压缩/解压缩数据。
  • 名称中包含 unicode 符号的文件夹和文件无法由 Shell.Application 对象处理。
  • 生成的 zip 文件的最大支持大小在 Vista 及以上版本中约为 8gb,在 XP/2003 中约为 2gb

脚本检测是否弹出错误消息并停止执行并告知可能的原因。目前我无法检测到弹出窗口中的文本并给出失败的确切原因。

3。 Makecab

压缩文件很容易 - makecab file.txt "file.cab" 。最终 MaxCabinetSize 可以增加。 压缩文件夹需要为每个(子)目录和 . 这是一个脚本:

;@echo off

;;;;; rem start of the batch part  ;;;;;
;
;for %%a in (/h /help -h -help) do ( 
;   if /I "%~1" equ "%%~a" if "%~2" equ "" (
;       echo compressing directory to cab file  
;       echo Usage:
;       echo(
;       echo %~nx0 "directory" "cabfile"
;       echo(
;       echo to uncompress use:
;       echo EXPAND cabfile -F:* .
;       echo(
;       echo Example:
;       echo(
;       echo %~nx0 "c:\directory\logs" "logs"
;       exit /b 0
;   )
; )
;
; if "%~2" EQU "" (
;   echo invalid arguments.For help use:
;   echo %~nx0 /h
;   exit /b 1
;)
;
; set "dir_to_cab=%~f1"
;
; set "path_to_dir=%~pn1"
; set "dir_name=%~n1" 
; set "drive_of_dir=%~d1"
; set "cab_file=%~2"
;
; if not exist %dir_to_cab%\ (
;   echo no valid directory passed
;   exit /b 1
;)

;
;break>"%tmp%\makecab.dir.ddf"
;
;setlocal enableDelayedExpansion
;for /d /r "%dir_to_cab%" %%a in (*) do (
;   
;   set "_dir=%%~pna"
;   set "destdir=%dir_name%!_dir:%path_to_dir%=!"
;   (echo(.Set DestinationDir=!destdir!>>"%tmp%\makecab.dir.ddf")
;   for %%# in ("%%a\*") do (
;       (echo("%%~f#"  /inf=no>>"%tmp%\makecab.dir.ddf")
;   )
;)
;(echo(.Set DestinationDir=!dir_name!>>"%tmp%\makecab.dir.ddf")
;   for %%# in ("%~f1\*") do (
;       
;       (echo("%%~f#"  /inf=no>>"%tmp%\makecab.dir.ddf")
;   )

;makecab /F "%~f0" /f "%tmp%\makecab.dir.ddf" /d DiskDirectory1=%cd% /d CabinetNameTemplate=%cab_file%.cab
;rem del /q /f "%tmp%\makecab.dir.ddf"
;exit /b %errorlevel%

;;
;;;; rem end of the batch part ;;;;;

;;;; directives part ;;;;;
;;
.New Cabinet
.set GenerateInf=OFF
.Set Cabinet=ON
.Set Compress=ON
.Set UniqueFiles=ON
.Set MaxDiskSize=1215751680;

.set RptFileName=nul
.set InfFileName=nul

.set MaxErrors=1
;;
;;;; end of directives part ;;;;;

示例用法:

call cabDir.bat ./myDir compressedDir.cab

解压可以使用EXPAND cabfile -F:* .,Unix解压可以使用cabextract7zip

4。 .NET 和 GZipStream

我更喜欢 Jscript.net,因为它允许与 .bat 进行巧妙的混合(没有有毒输出,也没有临时文件)。Jscript 不允许将对象的引用传递给函数,所以这是我发现的唯一方法工作是逐字节读/写文件(所以我想这不是最快的方法 - 如何进行缓冲读/写?)再次只能用于单个文件。

@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal

for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d  /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
   set "jsc=%%v"
)

if not exist "%~n0.exe" (
    "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)

 %~n0.exe %*

endlocal & exit /b %errorlevel%


*/


import System;
import System.Collections.Generic;
import System.IO;
import System.IO.Compression;


    
    function CompressFile(source,destination){
        var sourceFile=File.OpenRead(source);
        var destinationFile=File.Create(destination);
        var output = new  GZipStream(destinationFile,CompressionMode.Compress);
        Console.WriteLine("Compressing {0} to {1}.", sourceFile.Name,destinationFile.Name, false);
        var byteR = sourceFile.ReadByte();
        while(byteR !=- 1){
            output.WriteByte(byteR);
            byteR = sourceFile.ReadByte();
        }
        sourceFile.Close();
        output.Flush();
        output.Close();
        destinationFile.Close();
    }

    function UncompressFile(source,destination){
        var sourceFile=File.OpenRead(source);
        var destinationFile=File.Create(destination);
        
        var input = new GZipStream(sourceFile,
            CompressionMode.Decompress, false);
        Console.WriteLine("Decompressing {0} to {1}.", sourceFile.Name,
                destinationFile.Name);
        
        var byteR=input.ReadByte();
        while(byteR !== -1){
            destinationFile.WriteByte(byteR);
            byteR=input.ReadByte();
        }
        destinationFile.Close();
        input.Close();
        
        
    }
    
var arguments:String[] = Environment.GetCommandLineArgs();

    function printHelp(){
        Console.WriteLine("Compress and uncompress gzip files:");
        Console.WriteLine("Compress:");
        Console.WriteLine(arguments[0]+" -c source destination");
        Console.WriteLine("Uncompress:");
        Console.WriteLine(arguments[0]+" -u source destination");
        
        
    }

if (arguments.length!=4){
    Console.WriteLine("Wrong arguments");
    printHelp();
    Environment.Exit(1);
}

switch (arguments[1]){
    case "-c":
    
        CompressFile(arguments[2],arguments[3]);
        break;
    case "-u":
        UncompressFile(arguments[2],arguments[3]);
        break;
    default:
        Console.WriteLine("Wrong arguments");
        printHelp();
        Environment.Exit(1);
}

示例用法:

//zip
call netzip.bat -c my.file my.zip
//unzip
call netzip.bat -u my.zip my.file

5。 TAR(仅适用于最新的 windows 版本)

现在有了最新版本的 Windows 10,我们有了 TAR 命令,尽管它不是最向后兼容的选项:

//compress directory
tar -cvf archive.tar c:\my_dir
//extract to dir
tar -xvf archive.tar.gz -C c:\data
//compres to zip format
tar -caf archive.zip c:\my_dir

【讨论】:

  • 绝对是最全面的解决方案集合。
  • 这确实是一个很棒的解决方案集合。我最喜欢zipjs.bat 的解决方案2。但我对zipjs.bat 有一个增强请求。在 ZIP 文件的 解压缩 中,它会在 %TEMP% 中留下临时用于从 ZIP 文件中提取每个文件的子目录和文件。如果这个混合批处理文件能够自行删除它们会很好,请参阅我在How to write a batch file that can unzip a file into a new folder with the same name? 上的回答
  • @Mofi - 会检查这个。我没有在 temp 文件夹中创建 zip,而是 shell.application 的工作方式。我还有两个关于 zipjs.bat 的请求,我希望很快有时间更新它。
  • zipjs.bat 非常适合我,感谢您提供此脚本!只有选项 -keep no 似乎不可靠,有时源文件夹及其某些内容不会被删除。之后我自己执行删除 (RD)。
  • ZIP.CMD 和 UNZIP.cmd 链接都在该日期导致 404 错误。
【解决方案2】:

惊人的解决方案!

makecab 解决方案有一些问题,所以这里有一个固定版本,可以解决使用带有空格的目录时的问题。

;@echo off

;;;;; rem start of the batch part  ;;;;;
;
;for %%a in (/h /help -h -help) do ( 
;   if /I "%~1" equ "%%~a" if "%~2" equ "" (
;       echo compressing directory to cab file  
;       echo Usage:
;       echo(
;       echo %~nx0 "directory" "cabfile"
;       echo(
;       echo to uncompress use:
;       echo EXPAND cabfile -F:* .
;       echo(
;       echo Example:
;       echo(
;       echo %~nx0 "c:\directory\logs" "logs"
;       exit /b 0
;   )
; )
;
; if "%~2" EQU "" (
;   echo invalid arguments.For help use:
;   echo %~nx0 /h
;   exit /b 1
;)
;
; set "dir_to_cab=%~f1"
;
; set "path_to_dir=%~pn1"
; set "dir_name=%~n1" 
; set "drive_of_dir=%~d1"
; set "cab_file=%~2"
; 
; if not exist "%dir_to_cab%\" (
;   echo no valid directory passed
;   exit /b 1
;)

;
;break>"%tmp%\makecab.dir.ddf"
;
;setlocal enableDelayedExpansion
;for /d /r "%dir_to_cab%" %%a in (*) do (
;   
;   set "_dir=%%~pna"
;   set "destdir=%dir_name%!_dir:%path_to_dir%=!"
;   (echo(.Set DestinationDir=!destdir!>>"%tmp%\makecab.dir.ddf")
;   for %%# in ("%%a\*") do (
;       (echo("%%~f#"  /inf=no>>"%tmp%\makecab.dir.ddf")
;   )
;)
;(echo(.Set DestinationDir=!dir_name!>>"%tmp%\makecab.dir.ddf")
;   for %%# in ("%~f1\*") do (
;       
;       (echo("%%~f#"  /inf=no>>"%tmp%\makecab.dir.ddf")
;   )

;makecab /F "%~f0" /f "%tmp%\makecab.dir.ddf" /d DiskDirectory1="%cd%" /d CabinetNameTemplate=%cab_file%.cab
;rem del /q /f "%tmp%\makecab.dir.ddf"
;exit /b %errorlevel%

;;
;;;; rem end of the batch part ;;;;;

;;;; directives part ;;;;;
;;
.New Cabinet
.set GenerateInf=OFF
.Set Cabinet=ON
.Set Compress=ON
.Set UniqueFiles=ON
.Set MaxDiskSize=1215751680;

.set RptFileName=nul
.set InfFileName=nul

.set MaxErrors=1
;;
;;;; end of directives part ;;;;;

【讨论】:

    【解决方案3】:

    一些使用Compress-ArchiveExpand-Archive的powershell示例:

    //zipping folder or file:
    powershell "Compress-Archive -Path """C:\some_folder""" -DestinationPath """zippedFolder.zip""""
    
    //unzipping folder:
    powershell "Expand-Archive -Path """Draftv2.Zip""" -DestinationPath """C:\Reference""""
    

    与当前支持的 Windows 版本一样,这些 cmd-let 是默认安装的。

    powershell的另一种方式

    //zip directory
    powershell "Add-Type -Assembly """System.IO.Compression.FileSystem""" ;[System.IO.Compression.ZipFile]::CreateFromDirectory("""C:\some_dir""", """some.zip""");"
    
    //unzip directory
    powershell "Add-Type -Assembly "System.IO.Compression.FileSystem" ;[System.IO.Compression.ZipFile]::ExtractToDirectory("""yourfile.zip""", """c:\your\destination""");"
    

    【讨论】:

      【解决方案4】:

      CAB.bat [输入] 文件夹或文件:pack | .cab 或 .??_ : 解压 | none : 打包 files 子文件夹
      还将添加一个 CAB 条目以右键单击 SendTo 菜单以便于处理
      由于这一项可以无缝地完成这两项任务,因此它应该优于丑陋的 makecab 一项 - 如果您仍然写入临时文件,为什么要使用混合脚本?

      @echo off &echo. &set "ext=%~x1" &title CAB [%1] &rem input file or folder / 'files' folder / unpacks .cab .??_
      if "_%1"=="_" if not exist "%~dp0files" echo CAB: No input and no 'files' directory to pack &goto :Exit "do nothing"
      if "_%1"=="_" if exist "%~dp0files" call :CabDir "%~dp0files" &goto :Exit "input = none, use 'files' directory -pack" 
      for /f "tokens=1 delims=r-" %%I in ("%~a1") do if "_%%I"=="_d" call :CabDir "%~f1" &goto :Exit "input = dir -pack"
      if not "_%~x1"=="_.cab" if not "_%ext:~-1%"=="__" call :CabFile "%~f1" &goto :Exit "input = file -pack"
      call :CabExtract "%~f1" &goto :Exit "input = .cab or .??_ -unpack" 
      :Exit AveYo: script will add a CAB entry to right-click -- SendTo menu
      if not exist "%APPDATA%\Microsoft\Windows\SendTo\CAB.bat" copy /y "%~f0" "%APPDATA%\Microsoft\Windows\SendTo\CAB.bat" >nul 2>nul
      ping -n 6 localhost >nul &title cmd.exe &exit /b
      :CabExtract %1:[.cab or .xx_]
      echo %1 &pushd "%~dp1" &mkdir "%~n1" >nul 2>nul &expand -R "%~1" -F:* "%~n1" &popd &goto :eof
      :CabFile %1:[filename]
      echo %1 &pushd "%~dp1" &makecab /D CompressionType=LZX /D CompressionLevel=7 /D CompressionMemory=21 "%~nx1" "%~n1.cab" &goto :eof   
      :CabDir %1:[directory]
      dir /a:-D/b/s "%~1"
      set "ddf="%temp%\ddf""
      echo/.New Cabinet>%ddf%
      echo/.set Cabinet=ON>>%ddf%
      echo/.set CabinetFileCountThreshold=0;>>%ddf%
      echo/.set Compress=ON>>%ddf%
      echo/.set CompressionType=LZX>>%ddf%
      echo/.set CompressionLevel=7;>>%ddf%
      echo/.set CompressionMemory=21;>>%ddf%
      echo/.set FolderFileCountThreshold=0;>>%ddf%
      echo/.set FolderSizeThreshold=0;>>%ddf%
      echo/.set GenerateInf=OFF>>%ddf%
      echo/.set InfFileName=nul>>%ddf%
      echo/.set MaxCabinetSize=0;>>%ddf%
      echo/.set MaxDiskFileCount=0;>>%ddf%
      echo/.set MaxDiskSize=0;>>%ddf%
      echo/.set MaxErrors=1;>>%ddf%
      echo/.set RptFileName=nul>>%ddf%
      echo/.set UniqueFiles=ON>>%ddf%
      setlocal enabledelayedexpansion
      pushd "%~dp1"
      for /f "tokens=* delims=" %%D in ('dir /a:-D/b/s "%~1"') do (
       set "DestinationDir=%%~dpD" &set "DestinationDir=!DestinationDir:%~1=!" &set "DestinationDir=!DestinationDir:~0,-1!"
       echo/.Set DestinationDir=!DestinationDir!;>>%ddf%
       echo/"%%~fD"  /inf=no;>>%ddf%
      )
      makecab /F %ddf% /D DiskDirectory1="" /D CabinetNameTemplate=%~nx1.cab &endlocal &popd &del /q /f %ddf% &goto :eof
      

      【讨论】:

        【解决方案5】:

        作为@IIde 的回答,改进了@npocmaka 发布的makecab 批处理脚本,我也进一步改进了它。也就是说,我添加了一个 [-w/-wrapper] 选项作为第三个参数,并更改了与此相关的默认行为。

        原始脚本的工作方式是,源目录作为外部“包装器”/“容器”包含在生成的 cab 文件中。取而代之的是,我将递归收集的源目录的内容从存档的 root 开始放置。但是,如果您想保留嵌套,只需将 -w 作为最终参数传递给我的版本即可。

        此外,我将推荐的解压实用程序从EXPAND 更改为EXTRAC32。当“包装器”目录不在存档中时,前者被证明很难实现,后者可以毫不费力地解压缩所有内容。 (请注意,gui 实用程序,例如 7-zip 更容易实现此目的!)

        Dir2Cab.bat

        ;@echo off
        
        ;;;;; rem start of the batch part  ;;;;;
        ;
        ;for %%a in (/h /help -h -help) do ( 
        ;   if /I "%~1" equ "%%~a" if "%~2" equ "" (
        ;       echo compressing directory to cab file  
        ;       echo Usage:
        ;       echo(
        ;       echo %~nx0 "directory" "cabfile" [-w/-wrapper]
        ;       echo(
        ;       echo to uncompress use:
        ;       echo EXTRAC32 cabfile.cab /E /L .
        ;       echo(
        ;       echo Example:
        ;       echo(
        ;       echo %~nx0 "c:\directory\logs" "logs" 
        ;       exit /b 0
        ;   )
        ; )
        ;
        ; if "%~2" equ "" (
        ;   echo invalid arguments.For help use:
        ;   echo %~nx0 -h
        ;   exit /b 1
        ;)
        ;
        ; set "dir_to_cab=%~f1"
        ;
        ; set "path_to_dir=%~pn1"
        ; set "dir_name=%~n1" 
        ; set "drive_of_dir=%~d1"
        ; set "cab_file=%~2"
        ; 
        ; if not exist "%dir_to_cab%\" (
        ;   echo no valid directory passed
        ;   exit /b 1
        ;)
        
        ; :: Toggle the wrapper directory option
        ;set "wrapperdir="
        ;for %%a in (-w -wrapper) do ( 
        ;   if /I "%~3" equ "%%~a" set "wrapperdir=%dir_name%"
        ;)
        
        ;
        ;break>"%tmp%\makecab.dir.ddf"
        ;
        ;setlocal enableDelayedExpansion
        
        ;:: Generate a DDF file via a recursive directory listing
        ;for /d /r "%dir_to_cab%" %%a in (*) do (
        ;   
        ;   set "_dir=%%~pna"
        ;   set "destdir=%wrapperdir%!_dir:%path_to_dir%=!"
        ;   (echo(.Set DestinationDir=!destdir!>>"%tmp%\makecab.dir.ddf")
        ;   for %%# in ("%%a\*") do (
        ;       (echo("%%~f#"  /inf=no>>"%tmp%\makecab.dir.ddf")
        ;   )
        ;)
        ;(echo(.Set DestinationDir=!wrapperdir!>>"%tmp%\makecab.dir.ddf")
        ;   for %%# in ("%~f1\*") do (
        ;       (echo("%%~f#"  /inf=no>>"%tmp%\makecab.dir.ddf")
        ;   )
        
        ;:: Run makecab against the DDF file 
        ;makecab /F "%~f0" /f "%tmp%\makecab.dir.ddf" /d DiskDirectory1="%cd%" /d CabinetNameTemplate=%cab_file%.cab
        
        ;:: You might comment out this DDF file deletion for debugging purposes...
        ;del /q /f "%tmp%\makecab.dir.ddf"
        
        ;exit /b %errorlevel%
        
        ;;
        ;;;; rem end of the batch part ;;;;;
        
        ;;;; directives part ;;;;;
        ;;
        .New Cabinet
        .set GenerateInf=OFF
        .Set Cabinet=ON
        .Set Compress=ON
        .Set UniqueFiles=ON
        .Set MaxDiskSize=1215751680;
        
        .set RptFileName=nul
        .set InfFileName=nul
        
        .set MaxErrors=1
        ;;
        ;;;; end of directives part ;;;;;
        

        【讨论】:

          猜你喜欢
          • 2015-02-01
          • 1970-01-01
          • 2010-12-14
          • 2010-09-05
          • 2019-08-11
          • 1970-01-01
          • 1970-01-01
          • 2019-04-14
          • 1970-01-01
          相关资源
          最近更新 更多