Как можно сжать (/ zip) и распаковать (/ unzip) файлы и папки с командным файлом без использования каких-либо внешних инструментов?
Я знаю, что подобный вопрос задавали здесь много, но я не полностью удовлетворен ответами (и даже вопросами).
Основная цель - совместимость - она должна быть применима к как можно большему количеству машин с Windows (включая XP,Vista,Win2003 - которые вместе все еще занимают около 20% общего количества окон), а созданные файлы должны использоваться на машинах Unix/Mac (поэтому стандартно форматы архивирования / сжатия предпочтительны).
Какие есть варианты:
- Создание пакета, который реализует некоторый алгоритм почтового индекса. По-видимому, это возможно - но только с отдельными файлами и использованием CERTUTIL для двоичной обработки (на некоторых машинах по умолчанию отсутствует CERTUTIL, и его невозможно установить в WinXP Home Edition).
- Использование shell.application через WSH. Лучший вариант на мой взгляд. Он позволяет архивировать целые каталоги и может использоваться на любой машине с Windows.
- Makecab - несмотря на то, что его сжатие не так переносимо, оно доступно на любой машине с Windows. Некоторые внешние программы, такие как 7zip, способны извлекать контент.CAB, но это будет не очень удобно, когда файлы нужно использовать в Unix/Mac. И при сжатии Отдельный файл довольно прост, сохранение структуры каталогов требует немного больше усилий.
- Использование.NET Framework - не очень хороший вариант. В.NET 2.0 есть GZipStream, но он позволяет сжимать только отдельные файлы. .NET 4.5 имеет возможности Zip, но он не поддерживается в Vista и XP . И даже более того..NET не устанавливается по умолчанию в XP и Win2003, но, поскольку весьма вероятно иметь.NET 2.0 до 4.0, это значительный выбор,
- Powershell - поскольку он опирается на.NET, он имеет те же возможности. Он не установлен по умолчанию в XP,2003 и Vista, поэтому я его пропущу.
6 ответов
И вот ответ (ы):
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);
}
Потрясающие решения!
У решения 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 ;;;;;
Некоторые примеры использования PowerShell с использованием Compress-Archive и Expand-Archive:
//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""");"
В качестве ответа от @IIde, улучшенного пакетного скрипта makecab, опубликованного @npocmaka, я также немного развил его. А именно добавил
Как и в оригинальном сценарии, исходный каталог включен как внешняя «оболочка» / «контейнер» внутри результирующего CAB-файла. Вместо этого я разместил рекурсивно собранное содержимое исходного каталога, начиная с корня архива вниз. Однако, если вы хотите сохранить вложенность, вы просто передаете
Кроме того, я изменил рекомендуемую утилиту декомпрессии с
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 ;;;;;
CAB.bat [входная] папка или файл: pack | .cab или.??_: распаковать | нет: упаковать 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
Рабочий прототип с makeself , certutil и busybox:
https://github.com/hemnstill/makeself/releases/tag/release-2.4.5-cmd
плюсы:
- одинокий
.bat
файл, который работает в Windows 2016+ и многих Linux. - впечатляющие аргументы командной строки. потенциально поддерживать различные форматы архивов.
- никаких внешних зависимостей. только
certuril.exe
используется для извлечения
минусы:
- размер полезной нагрузки ~900 КБ (может быть немного уменьшен с помощью upx)
- Результат virustotal: 9/56 (антивирусам не нравится, когда для извлечения полезной нагрузки используется certutil.exe)
-
busybox.exe
не поддерживает все функции makeself cli.