Как распаковать файл в Powershell?

224

У меня есть .zipфайл, и мне нужно распаковать весь его контент с помощью Powershell. Я делаю это, но это не похоже на работу:

$shell = New-Object -ComObject shell.application
$zip = $shell.NameSpace("C:\a.zip")
MkDir("C:\a")
foreach ($item in $zip.items()) {
  $shell.Namespace("C:\a").CopyHere($item)
}

В чем дело? Каталог C:\aпо-прежнему пуст.

Ули Кункель
источник
6
Если вы работаете в Powershell 2.0 или без установленного .NET 4.5, то упомянутый вами метод является единственным путем (без использования стороннего exe-файла (например, 7zip). Я бы сказал, что на этот вопрос не ответили полностью, пока кто-то объясняет, почему этот метод не работает. Иногда он работает для меня, а кто-то - нет.
Kenny

Ответы:

248

Вот простой способ использования ExtractToDirectory из System.IO.Compression.ZipFile :

Add-Type -AssemblyName System.IO.Compression.FileSystem
function Unzip
{
    param([string]$zipfile, [string]$outpath)

    [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)
}

Unzip "C:\a.zip" "C:\a"

Обратите внимание, что если целевая папка не существует, ExtractToDirectory создаст ее. Другие предостережения:

  • Существующие файлы не будут перезаписаны, а вызовут исключение IOException.
  • Для этого метода требуется как минимум .NET Framework 4.5, доступная для Windows Vista и новее.
  • Относительные пути не разрешаются в зависимости от текущего рабочего каталога, см. Почему объекты .NET в PowerShell не используют текущий каталог?

Смотрите также:

Микки Балладелли
источник
10
Почему вы создаете функцию для замены одного вызова функции?
17
Теоретически нет. Я пытаюсь скрыть сложные / нетрадиционные вызовы в функциях, чтобы позже я мог заменить метод, не беспокоясь о том, где он используется. Как упоминал Кит, в V5 появится новый способ сделать это.
Микки Балладелли
1
Для этого вам нужен как минимум .NET Framework 4.5. Смотрите в нижней части msdn.microsoft.com/en-us/library/…
ferventcoder
10
Я пробовал это, но получаю ошибку ниже, Exception calling "ExtractToDirectory" with "2" argument(s): "End of Central Directory record could not be found." At line:5 char:5 + [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $ou ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : InvalidDataException
Karthi1234
4
Это дает мне ошибку следующее: Add-Type : Cannot add type. The assembly 'System.IO.Compression.FileSystem' could not be found.. У меня установлен .NET 4.6.2, и я проверил, что сборка находится в GAC, но я не понял, почему я получаю эту ошибку.
Сэм
500

В PowerShell v5 + есть команда Expand-Archive (а также Compress-Archive), встроенная в:

Expand-Archive c:\a.zip -DestinationPath c:\a
Кит Хилл
источник
26
Используйте, $PSVersionTable.PSVersionчтобы определить, какую версию PowerShell вы используете.
Брэд C
1
@LoneCoder Не думаю, что ты можешь винить Баллмера. В Windows никогда раньше не было встроенного инструмента командной строки для обработки сжатых файлов, хотя gzip вышел в 1992 году, а tar еще старше.
jpmc26
2
@Ghashange PowerShell 5 даже не был доступен ни для чего, кроме Windows 10 и Server 2012, когда был опубликован этот ответ, даже в качестве предварительной версии.
jpmc26
11
Похоже, что параметр OutputPathбыл изменен на DestinationPath(ссылка msdn.microsoft.com/powershell/reference/5.1/… )
Элайджа В. Ганье
1
Вы также можете использовать относительные пути, такие какExpand-Archive -Path .\a.zip -DestinationPath .
Culip
24

В PowerShell v5.1 это немного отличается от v5. Согласно документации MS, он должен иметь -Pathпараметр для указания пути к файлу архива.

Expand-Archive -Path Draft.Zip -DestinationPath C:\Reference

Или же, это может быть реальный путь:

Expand-Archive -Path c:\Download\Draft.Zip -DestinationPath C:\Reference

Развернуть-Архив Док

NIK
источник
3
В этом командлете нет разницы между v5 и v5.1. Вам не нужно называть первый параметр; это автоматически станет путем. Например, Expand-Archive Draft.Zip -DestinationPath C:\Referenceработает без проблем. Кроме того, это не фактический путь, а абсолютный путь.
Франклин Ю
13

Используйте Expand-Archiveкомандлет с одним из набора параметров:

Expand-Archive -LiteralPath C:\source\file.Zip -DestinationPath C:\destination
Expand-Archive -Path file.Zip -DestinationPath C:\destination
Салех Рахимзаде
источник
12

Эй, это работает для меня ..

$shell = New-Object -ComObject shell.application
$zip = $shell.NameSpace("put ur zip file path here")
foreach ($item in $zip.items()) {
  $shell.Namespace("destination where files need to unzip").CopyHere($item)
}
Abhijit
источник
2
Если один из файлов или каталогов уже существует в месте назначения, появляется всплывающее диалоговое окно с вопросом, что делать (игнорировать, перезаписать), что противоречит цели. Кто-нибудь знает, как заставить его молча перезаписать?
Олег Казаков
Отвечая на комментарий @OlegKazakov: есть набор опций, управляющих CopyHereметодом. Я думаю, что @OlegKazakov уже решил свою проблему. Тем не менее, я разместил эту ссылку здесь для других пользователей, которые могут найти эту тему: docs.microsoft.com/en-us/previous-versions/windows/desktop/…
jsxt
4

Для тех, кто хочет использовать Shell.Application.Namespace.Folder.CopyHere () и хочет скрыть индикаторы выполнения при копировании или использовать дополнительные параметры, документация находится здесь:
https://docs.microsoft.com/en-us / окна / настольные / оболочка / папка-copyhere

Чтобы использовать powershell, скрывать индикаторы выполнения и отключать подтверждения, вы можете использовать код, подобный следующему

# We should create folder before using it for shell operations as it is required
New-Item -ItemType directory -Path "C:\destinationDir" -Force

$shell = New-Object -ComObject Shell.Application
$zip = $shell.Namespace("C:\archive.zip")
$items = $zip.items()
$shell.Namespace("C:\destinationDir").CopyHere($items, 1556)

Ограничения использования Shell.Application для основных версий Windows:
https://docs.microsoft.com/en-us/windows-server/administration/server-core/what-is-server-core

В основных версиях Windows по умолчанию Microsoft-Windows-Server-Shell-Package не установлен, поэтому shell.applicaton не будет работать.

примечание : извлечение архивов таким способом займет много времени и может замедлить работу графического интерфейса Windows.

Матей Ридзон
источник
3

Используя, expand-archiveно автоматически создаваемые каталоги, названные в честь архива:

function unzip ($file) {
    $dirname = (Get-Item $file).Basename
    New-Item -Force -ItemType directory -Path $dirname
    expand-archive $file -OutputPath $dirname -ShowProgress
}
mikemaccana
источник
Это обязательно расширяется в текущем каталоге, не так ли?
jpmc26
На самом деле не вижу добавленной стоимости автоматического создания. Более гибкий способ добавить второй параметр, outputPathкак в принятом ответе. В этом решении (как это jpmc26 сказал), вы всегда будете создавать новый каталог в текущем каталоге , так что возможно вам нужно установить текущий каталог перед вызовомunzip
Рубанов
Большинство архиваторов извлекаются в каталог, названный в честь архива, в том же месте, что и архив. Ничто не мешает вам добавлять параметры, если вы хотите что-то другое, но это разумное значение по умолчанию.
mikemaccana
1
function unzip {
    param (
        [string]$archiveFilePath,
        [string]$destinationPath
    )

    if ($archiveFilePath -notlike '?:\*') {
        $archiveFilePath = [System.IO.Path]::Combine($PWD, $archiveFilePath)
    }

    if ($destinationPath -notlike '?:\*') {
        $destinationPath = [System.IO.Path]::Combine($PWD, $destinationPath)
    }

    Add-Type -AssemblyName System.IO.Compression
    Add-Type -AssemblyName System.IO.Compression.FileSystem

    $archiveFile = [System.IO.File]::Open($archiveFilePath, [System.IO.FileMode]::Open)
    $archive = [System.IO.Compression.ZipArchive]::new($archiveFile)

    if (Test-Path $destinationPath) {
        foreach ($item in $archive.Entries) {
            $destinationItemPath = [System.IO.Path]::Combine($destinationPath, $item.FullName)

            if ($destinationItemPath -like '*/') {
                New-Item $destinationItemPath -Force -ItemType Directory > $null
            } else {
                New-Item $destinationItemPath -Force -ItemType File > $null

                [System.IO.Compression.ZipFileExtensions]::ExtractToFile($item, $destinationItemPath, $true)
            }
        }
    } else {
        [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory($archive, $destinationPath)
    }
}

С помощью:

unzip 'Applications\Site.zip' 'C:\inetpub\wwwroot\Site'
user1624251
источник
Не забывайте распоряжаться $archiveи $archiveFileв конце
Tom.maruska
0

ForEachЦикл обрабатывает каждый файл ZIP, расположенный в $filepathпеременной

    foreach($file in $filepath)
    {
        $zip = $shell.NameSpace($file.FullName)
        foreach($item in $zip.items())
        {
            $shell.Namespace($file.DirectoryName).copyhere($item)
        }
        Remove-Item $file.FullName
    }
Прадйумна
источник