Como descompactar um arquivo no Powershell?

224

Eu tenho um .ziparquivo e preciso descompactar todo o seu conteúdo usando o Powershell. Estou fazendo isso, mas parece não funcionar:

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

O que há de errado? O diretório C:\aainda está vazio.

Uli Kunkel
fonte
6
Se você estiver no Powershell 2.0 ou sem o .NET 4.5 instalado, o método que você mencionou é o único caminho (sem usar um exe de terceiros (por exemplo, 7zip) .Eu diria que a pergunta não será totalmente respondida até alguém fornece por isso que este método não funciona ele faz para mim por algum tempo, mas outros não..
Kenny

Respostas:

248

Aqui está uma maneira simples de usar o ExtractToDirectory de 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"

Observe que, se a pasta de destino não existir, o ExtractToDirectory a criará. Outras advertências:

  • Os arquivos existentes não serão substituídos e, em vez disso, acionarão uma IOException.
  • Este método requer pelo menos o .NET Framework 4.5, disponível para Windows Vista e mais recente.
  • Os caminhos relativos não são resolvidos com base no diretório de trabalho atual, consulte Por que os objetos .NET no PowerShell não usam o diretório atual?

Veja também:

Micky Balladelli
fonte
10
Por que você cria uma função para substituir uma única chamada de função?
17
Em teoria você não. Tento ocultar chamadas complexas / não convencionais em funções para poder substituir o método mais tarde sem me preocupar com o local onde ele é usado. Como Keith mencionou, na V5 haverá uma nova maneira de fazer isso.
Micky Balladelli
1
Você precisa pelo menos do .NET Framework 4.5 para isso. Consulte a parte inferior do arquivo msdn.microsoft.com/en-us/library/…
ferventcoder 14/12/16
10
Eu tentei isso, mas ficando abaixo do erro: 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
Isso me dá o seguinte erro: Add-Type : Cannot add type. The assembly 'System.IO.Compression.FileSystem' could not be found.. Tenho o .NET 4.6.2 instalado e verifiquei que o assembly está no GAC, mas não entendi por que estou recebendo esse erro.
Sam
500

No PowerShell v5 +, há um comando Expand-Archive (assim como o Compress-Archive) incorporado:

Expand-Archive c:\a.zip -DestinationPath c:\a
Keith Hill
fonte
26
Use $PSVersionTable.PSVersionpara determinar qual versão do PowerShell você está executando.
Brad C
1
@LoneCoder Eu não acho que você pode culpar Ballmer. O Windows nunca teve uma ferramenta de linha de comando incorporada para lidar com arquivos compactados antes, embora o gzip tenha sido lançado em 1992 e o tar seja ainda mais antigo.
jpmc26
2
O @Ghashange PowerShell 5 não estava disponível para nada abaixo do Windows 10 e Server 2012 quando esta resposta foi publicada, mesmo como um pré-lançamento.
jpmc26
11
Parece que o parâmetro OutputPathfoi alterado para DestinationPath(referência msdn.microsoft.com/powershell/reference/5.1/… )
Elijah W. Gagne
1
Você também pode usar caminhos relativos, como # Expand-Archive -Path .\a.zip -DestinationPath .
Culip
24

No PowerShell v5.1, isso é um pouco diferente em comparação à v5. De acordo com a documentação da Microsoft, ele precisa ter um -Pathparâmetro para especificar o caminho do arquivo morto.

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

Ou então, este pode ser um caminho real:

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

Documento Expand-Archive

NIK
fonte
3
Não há diferença entre v5 e v5.1 neste cmdlet. Você não precisa nomear o primeiro parâmetro; ele se tornará automaticamente o caminho. Por exemplo, Expand-Archive Draft.Zip -DestinationPath C:\Referencefunciona sem problemas. Além disso, não é um caminho real , mas um caminho absoluto .
Franklin Yu
13

Use o Expand-Archivecmdlet com um dos conjuntos de parâmetros:

Expand-Archive -LiteralPath C:\source\file.Zip -DestinationPath C:\destination
Expand-Archive -Path file.Zip -DestinationPath C:\destination
Saleh Rahimzadeh
fonte
12

Ei, está trabalhando para mim ..

$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
fonte
2
Se um dos arquivos ou diretórios já existir no local de destino, ele abrirá uma caixa de diálogo perguntando o que fazer (ignorar, substituir), o que anula o objetivo. Alguém sabe como forçá-lo a substituir silenciosamente?
precisa
Respondendo ao comentário de @OlegKazakov: existe um conjunto de opções que controlam o CopyHeremétodo. Acho que o @OlegKazakov já resolveu o problema. No entanto eu coloquei este link aqui para outros surfistas que pode encontrar este tópico: docs.microsoft.com/en-us/previous-versions/windows/desktop/...
jsxt
4

Para aqueles que desejam usar Shell.Application.Namespace.Folder.CopyHere () e desejam ocultar barras de progresso durante a cópia ou usar mais opções, a documentação está aqui:
https://docs.microsoft.com/en-us / windows / desktop / shell / copy-folderaqui

Para usar o PowerShell e ocultar as barras de progresso e desativar as confirmações, você pode usar um código como este:

# 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)

Limitações do uso do Shell.Application nas versões principais do Windows:
https://docs.microsoft.com/en-us/windows-server/administration/server-core/what-is-server-core

Nas versões do Windows Core , por padrão, o Microsoft-Windows-Server-Shell-Package não está instalado, portanto, o shell.applicaton não funcionará.

Nota : A extração de arquivos dessa maneira levará um longo tempo e pode reduzir a velocidade da interface do Windows

Matej Ridzon
fonte
3

Usando, expand-archivemas criando diretórios automaticamente, nomeados após o archive:

function unzip ($file) {
    $dirname = (Get-Item $file).Basename
    New-Item -Force -ItemType directory -Path $dirname
    expand-archive $file -OutputPath $dirname -ShowProgress
}
mikemaccana
fonte
Isso necessariamente se expande no diretório atual, não é?
precisa saber é o seguinte
Realmente não vejo o valor agregado da criação automática. É mais flexível adicionar um segundo parâmetro outputPathcomo na resposta aceita. Nesta solução (como disse o jpmc26), você sempre criará um novo diretório no diretório atual, portanto é possível definir o diretório atual antes de ligarunzip
Rubanov
A maioria dos arquivadores é extraída em um diretório com o nome do arquivo, no mesmo local que o arquivo. Nada para impedir que você adicione parâmetros se quiser algo diferente, mas é um padrão sensato.
Mikemaccana 6/07
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)
    }
}

Usando:

unzip 'Applications\Site.zip' 'C:\inetpub\wwwroot\Site'
user1624251
fonte
Não se esqueça de dispor $archivee $archiveFileno final
tom.maruska
0

ForEachO loop processa cada arquivo ZIP localizado na $filepathvariável

    foreach($file in $filepath)
    {
        $zip = $shell.NameSpace($file.FullName)
        foreach($item in $zip.items())
        {
            $shell.Namespace($file.DirectoryName).copyhere($item)
        }
        Remove-Item $file.FullName
    }
Pradyumna
fonte