Wie kann ich Dateien und Ordner mit Batch-Dateien komprimieren (/ zippen) und dekomprimieren (/ entpacken), ohne externe Tools zu verwenden?

Ich weiß, dass die ähnliche Frage hier sehr oft gestellt wurde, aber ich bin nicht ganz zufrieden mit den Antworten (und sogar mit den Fragen).

Das Hauptziel ist die Kompatibilität – es sollte für eine breite Palette von Windows-Maschinen (einschließlich XP, Vista, Win2003 – die zusammen immer noch etwa 20% der Windows-Freigabe hält) und produzierten Dateien auf Unix / Mac-Maschinen (Standard) verwendet werden Archivierungs- / Komprimierungsformate sind vorzuziehen).

Was sind die Optionen:

  1. Erstellen eines Stapels, der einen ZIP-Algorithmus implementiert. Scheinbar ist das möglich – aber nur mit einzelnen Dateien und mit CERTUTIL für die Binärverarbeitung (einige Maschinen haben standardmäßig kein CERTUTIL und können nicht auf WinXP Home Edition installiert werden)
  2. Verwenden von shell.application über WSH. Die beste Option für mich. Sie ermöglicht das Zippen ganzer Verzeichnisse und ist auf jedem Windows-Rechner verwendbar
  3. Makecab – trotz seiner Komprimierung ist es nicht so portabel, dass es auf jedem Windows-Rechner verfügbar ist. Einige externe Programme wie 7zip können .CAB-Inhalte extrahieren, aber es ist nicht so praktisch, wenn Dateien unter Unix / Mac verwendet werden müssen Eine einzelne Datei ist ziemlich einfach, die Beibehaltung der Verzeichnisstruktur erfordert ein wenig mehr Aufwand.
  4. Mit .NET Framework – nicht so gute Option.Form .NET 2.0 gibt es GZipStream, aber es erlaubt nur die Komprimierung von einzelnen Dateien. .NET 4.5 hat Zip-functionen, aber es wird nicht unter Vista und XP unterstützt. Und noch mehr – .NET ist nicht standardmäßig auf XP und Win2003 installiert, aber da es sehr wahrscheinlich ist, .NET 2.0 bis zu 4.0 zu haben, ist dies eine beachtliche Option .
  5. Powershell – da es auf .NET basiert, hat es die gleichen Fähigkeiten. Es ist nicht standardmäßig auf XP, 2003 und Vista installiert, also werde ich es überspringen.

Und hier sind die Antwort (en):

1.Verwendung von “reinem” Batch-Skript zum Zip / Entpacken der Datei.

Dies ist möglich dank Frank Westlakes ZIP.CMD und UNZIP.CMD (benötigt Administratorrechte und benötigt FSUTIL und CERTUTIL ). Für FSUTIL und CERTUTIL wird das 2003 Admin Tool Pack benötigt, welches CERTUTIL installiert. Seien Sie vorsichtig, da die ZIP.CMD-Syntax rückwärts ist:

 ZIP.CMD destination.zip source.file 

Und es kann nur einzelne Dateien zippen.

2. Verwenden von Shell.Application

Ich habe einige Zeit damit verbracht, ein einzelnes Skript für den JScript / Batch-Hybrid zu erstellen, das Dateien und Verzeichnisse zippt / entzippt (plus ein paar weitere functionen). Hier ist ein Link dazu (es wurde zu groß, um in der Antwort zu posten). Kann direkt mit seiner Erweiterung .bat und erstellt keine temporären Dateien. Ich hoffe, dass die Hilfemeldung beschreibend genug ist, wie sie verwendet werden kann.

Einige Beispiele:

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

Einige bekannte Probleme beim Zippen:

  • Wenn auf dem Systemlaufwerk nicht genügend Speicherplatz vorhanden ist (normalerweise C :), kann das Skript verschiedene Fehler verursachen, am häufigsten stoppt das Skript. Dies liegt daran, dass Shell.Application den Ordner % TEMP% zum Komprimieren / Dekomprimieren der Daten aktiv verwendet.
  • Ordner und Dateien, die Unicode-Symbole in ihren Namen enthalten, können nicht vom Shell.Application-Objekt verarbeitet werden.
  • Die maximal unterstützte Größe der produzierten Zip-Dateien beträgt in Vista und höher etwa 8 GB und in XP / 2003 etwa 2 GB

Das Skript erkennt, ob eine Fehlermeldung angezeigt wird, stoppt die Ausführung und informiert über die möglichen Ursachen. Im Moment habe ich keine Möglichkeit, den Text im Popup zu erkennen und den genauen Grund für den Fehler anzugeben.

3. Makecab .

Komprimieren einer Datei ist einfach – makecab file.txt "file.cab" . Schließlich könnte MaxCabinetSize erhöht werden. Das Komprimieren eines Ordners erfordert eine Verwendung der Direktive DestinationDir (mit relativen Pfaden) für jedes (Unter) Verzeichnis und die darin enthaltenen Dateien. Hier ist ein Skript:

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

Zur Dekomprimierung EXPAND cabfile -F:* . kann verwendet werden. Für die Extraktion in Unix kann cabextract oder 7zip verwendet werden.

4. .NET und GZipStream

Ich bevorzuge ein Jscript.net, da es eine ordentliche Hybridisierung mit .bat erlaubt (keine toxische Ausgabe und keine temporären Dateien) .JScript erlaubt es nicht, eine Referenz eines Objekts an eine function zu übergeben, also ist die einzige Art, wie ich es gefunden habe, zu funktionieren Lesen / Schreiben von Dateien byteweise (also nehme ich an, es ist nicht der schnellste Weg – wie gepuffertes Lesen / Schreiben kann getan werden?) Wiederum kann nur mit einzelnen Dateien verwendet werden.

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

erstaunliche Lösungen!

Die makecab-Lösung hat einige Probleme, daher ist hier eine feste Version, die das Problem triggers, wenn Verzeichnisse mit Leerzeichen verwendet werden.

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

CAB.bat [Eingabe] Ordner oder Datei: pack | .cab oder. ?? _: auspacken | none: packe einen Unterordner für files
Fügt auch einen CAB Eintrag hinzu, um mit der rechten Maustaste auf SendTo-Menü zu klicken, um die Handhabung zu erleichtern
Da dieser beide Aufgaben nahtlos erledigt, sollte es gegenüber dem hässlichen Makecab bevorzugt werden – warum sollte man hybrides Skript verwenden, wenn man sowieso in eine temporäre Datei schreibt?

 @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