Как сжать (/zip) и распаковать (/распаковать) файлы и папки с помощью пакетного файла без использования каких-либо внешних инструментов?

Я знаю, что подобный вопрос задавали здесь много, но я не вполне доволен ответами (и даже с вопросами).

Основная цель - совместимость - она ​​должна быть применима к максимально возможному диапазону оконных машин (включая XP, Vista, Win2003, которые все еще содержат около 20% доли Windows) и производится файлы должны использоваться на компьютерах Unix/Mac (поэтому предпочтительны стандартные форматы архивирования/сжатия).

Какие существуют опции:

  • Создание пакета, который реализует некоторый zip-алгоритм. По-видимому, это возможно - но только с отдельными файлами и с использованием CERTUTIL для двоичная обработка (некоторые машины не имеют CERTUTIL по умолчанию и не может быть установлен на WinXP Home Edition)
  • Использование shell.application через WSH. Лучший вариант в соответствии с для меня. Это позволяет застегивать целые каталоги и может использоваться на каждом окна машины
  • Makecab - несмотря на то, что его сжатие не настолько переносимо доступны на всех машинах Windows. Некоторые внешние программы, такие как 7zip способны извлекать содержимое CAB, но это будет не так удобно, когда файлы должны использоваться в Unix/Mac.And while сжатие одного файла довольно просто, сохраняя структура каталогов требует немного больших усилий.
  • Использование .NET Framework - не очень хороший вариант .Form.NET 2.0 есть GZipStream, но он позволяет сжатие только отдельных файлов..СЕТЬ 4.5 имеет возможности Zip, но он не поддерживается в Vista и XP. И даже больше -.NET по умолчанию не установлен на XP и Win2003, но так как очень вероятно, что .NET 2.0 до 4.0 он значительный опиум.
  • Powershell - поскольку он полагается на .NET, он обладает такими же возможностями. не установлен по умолчанию в XP, 2003 и Vista, поэтому я пропущу его.

Ответ 1

И вот ответ (ы):

1.Использовать "чистый" пакетный скрипт для архивирования/распаковки файла.

Это возможно благодаря Фрэнку Уэстлейку ZIP.CMD и UNZIP.CMD(требуются права администратора и требуются FSUTIL и CERTUTIL). Для Win2003 и WinXP потребуется 2003 Admin Tool Pack , который установит CERTUTIL. Будьте осторожны, поскольку синтаксис ZIP.CMD обратный:

ZIP.CMD destination.zip source.file

И он может архивировать только отдельные файлы.

2. Использование Shell.Application

Я потратил некоторое время на создание единого гибридного сценария jscript/batch для общего использования, в котором архивируются и распаковываются файлы и каталоги (плюс еще несколько функций).Здесь ссылка на него (он стал слишком большим для публикации в ответ). Может использоваться напрямую с расширением .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. Приложение активно использует папку % TEMP% для сжатия/распаковки данные.
  • Папки и файлы, содержащие в своих именах символы Юникода, не могут обрабатываться объектом Shell.Application.
  • Максимальный поддерживаемый размер создаваемых zip файлов составляет около 8 ГБ в Vista и выше и около 2 ГБ в XP/2003

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

3.Makecab.

Сжать файл просто - makecab file.txt "file.cab". В конечном итоге MaxCabinetSize может быть увеличено. Сжатие папки требует использования директивы DestinationDir (с относительными путями) для каждого (под) каталога и файлов в нем. Вот скрипт:

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

Для распаковки можно использовать EXPAND cabfile -F:* .. Для извлечения в Unix можно использовать cabextract или 7zip.

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

Ответ 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

CAB.bat [вход] папка или файл: pack |.cab или.?? _: распаковать | none: упаковать a files вложенную папку
Также добавит элемент CAB, чтобы щелкнуть меню SendTo правой кнопкой мыши для удобства управления
Поскольку этот вопрос выполняет обе задачи без проблем, он должен быть предпочтительнее уродливого makecab - зачем использовать гибридный script, если вы все равно пишете в файл temp?

@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