How can I compress (/ zip ) and uncompress (/ unzip ) files and folders with batch file without using any external tools?

前端 未结 3 867
情深已故
情深已故 2020-11-22 02:39

I know the similar questions were asked here a lot, but I\'m not completely satisfied with the answers (and even with the questions).

The main goal is compatibility

相关标签:
3条回答
  • 2020-11-22 03:04

    CAB.bat [input] folder or file : pack | .cab or .??_ : unpack | none : pack a files subfolder
    Will also add a CAB entry to right-click SendTo menu for easy handling
    Since this one does both tasks seamlessly, it should be preferred over the ugly makecab one - why use hybrid script if you write to temp file anyway?

    @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
    
    0 讨论(0)
  • 2020-11-22 03:11

    amazing solutions!

    The makecab solution has some issues so here is a fixed version that solves the problem when using directories with blank spaces.

    ;@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 ;;;;;
    
    0 讨论(0)
  • 2020-11-22 03:16

    And here are the answer(s):

    1. Using "pure" batch script to zip/unzip file.

    It's possible thanks to Frank Westlake's ZIP.CMD and UNZIP.CMD(needs admin permissions and requires FSUTIL and CERTUTIL) .For Win2003 and WinXP it will require 2003 Admin Tool Pack which will install CERTUTIL. Be careful as ZIP.CMD syntax is backward :

    ZIP.CMD destination.zip source.file
    

    And it can zip only single files.

    2. Using Shell.Application

    I've spent some time to create a single jscript/batch hybrid script for common usage that zips/unzips files and directories (plus few more features).Here's a link to it (it became too big to post in the answer). Can be used directly with its .bat extension and does not create any temp files. I hope the help message is descriptive enough of how it can be used.

    Some examples:

    // 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
    

    Some known issues during zipping:

    • if there's not enough space on the system drive (usually C:) the script could produce various errors , most often the script halts.This is due to Shell.Application actively uses %TEMP% folder to compress/decompress the data.
    • Folders and files that contain unicode symbols in their names cannot be handled by Shell.Application object.
    • Max supported size of produced zip files is around 8gb in Vista and above and around 2gb in XP/2003

    The script detects if error message pops-up and stops the execution and informs for the possible reasons.At the moment I have no way to detect the text inside the pop-up and give the exact reason for the failure.

    3. Makecab

    Compressing a file is easy - makecab file.txt "file.cab" . Eventually MaxCabinetSize could be increased. Compressing a folder requires a usage DestinationDir directive (with relative paths) for every (sub)directory and the files within . Here's a script:

    ;@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 ;;;;;
    

    Example usage:

    call cabDir.bat ./myDir compressedDir.cab
    

    For decompression EXPAND cabfile -F:* . can be used.For extraction in Unix cabextract or 7zip can be used.

    4. .NET and GZipStream

    I preferred a Jscript.net as it allows a neat hybridization with .bat (no toxic output , and no temp files).Jscript does not allow passing a reference of object to a function so the only way I found to make it work is by reading/writing files byte by byte (so I suppose it's not the fastest way - how buffered reading/writing can be done?)Again can be used only with single files.

    @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);
    }
    

    Example usage:

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

    5. TAR (only for the newest windows builds)

    With the latest builds of windows 10 now we have TAR command ,though it's not the most backward compatible option:

    //compress directory
    tar -cvf archive.tar c:\my_dir
    //extract to dir
    tar -xvf archive.tar.gz -C c:\data
    
    0 讨论(0)
提交回复
热议问题