Jak rozpakować plik w programie Powershell?

224

Mam .zipplik i muszę rozpakować całą jego zawartość za pomocą programu Powershell. Robię to, ale wydaje się, że to nie działa:

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

Co jest nie tak? Katalog C:\ajest nadal pusty.

Uli Kunkel
źródło
6
Jeśli korzystasz z Powershell 2.0 lub nie masz .NET 4.5, to wspomniana metoda jest jedyną ścieżką (bez korzystania z exe innych firm (tj. 7zip). Powiedziałbym, że odpowiedź na to pytanie nie jest pełna, dopóki ktoś wyjaśnia, dlaczego ta metoda nie działa. Czasami mi się to udaje, ale innym się nie udaje
kenny

Odpowiedzi:

248

Oto prosty sposób przy użyciu ExtractToDirectory z 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"

Zauważ, że jeśli folder docelowy nie istnieje, ExtractToDirectory go utworzy. Inne zastrzeżenia:

Zobacz też:

Micky Balladelli
źródło
10
Dlaczego tworzysz funkcję zastępującą pojedyncze wywołanie funkcji?
17
Teoretycznie nie. Próbuję ukryć złożone / niekonwencjonalne wywołania w funkcjach, aby później móc zastąpić metodę bez obawy o to, gdzie jest używana. Jak wspomniał Keith, w V5 pojawi się nowy sposób na zrobienie tego.
Micky Balladelli,
1
Potrzebujesz do tego przynajmniej .NET Framework 4.5. Zobacz dół strony msdn.microsoft.com/en-us/library/…
ferventcoder
10
Próbowałem tego, ale wystąpił błąd poniżej 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
To daje mi błąd następujące: Add-Type : Cannot add type. The assembly 'System.IO.Compression.FileSystem' could not be found.. Mam zainstalowany program .NET 4.6.2 i sprawdziłem, czy zestaw znajduje się w GAC, ale nie zrozumiałem, dlaczego pojawia się ten błąd.
Sam
500

W programie PowerShell v5 + wbudowane jest polecenie Rozwiń-Archiwum (a także Kompresuj-Archiwum):

Expand-Archive c:\a.zip -DestinationPath c:\a
Keith Hill
źródło
26
Służy $PSVersionTable.PSVersiondo określania używanej wersji programu PowerShell.
Brad C
1
@LoneCoder Nie sądzę, że możesz winić Ballmera. System Windows nigdy wcześniej nie miał wbudowanego narzędzia wiersza poleceń do obsługi plików skompresowanych, mimo że gzip wyszedł w 1992 roku, a tar jest jeszcze starszy.
jpmc26
2
@Ghashange PowerShell 5 nie był nawet dostępny dla niczego poniżej Windows 10 i Server 2012, kiedy ta odpowiedź została opublikowana, nawet w wersji wstępnej.
jpmc26
11
Wygląda na to, że parametr OutputPathzostał zmieniony na DestinationPath(odniesienie msdn.microsoft.com/powershell/reference/5.1/… )
Elijah W. Gagne
1
Możesz także użyć ścieżek względnych, takich jakExpand-Archive -Path .\a.zip -DestinationPath .
Culip,
24

W PowerShell v5.1 jest to nieco inne w porównaniu do v5. Zgodnie z dokumentacją MS musi mieć -Pathparametr określający ścieżkę do pliku archiwum.

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

Albo może to być faktyczna ścieżka:

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

Rozwiń-Archiwum Dokumentów

NIK
źródło
3
W tym poleceniu cmdlet nie ma różnicy między wersją 5 a wersją 5.1. Nie musisz nazywać pierwszego parametru; automatycznie stanie się ścieżką. Na przykład Expand-Archive Draft.Zip -DestinationPath C:\Referencedziała bez problemu. Ponadto nie jest to ścieżka rzeczywista , ale ścieżka bezwzględna .
Franklin Yu,
13

Użyj polecenia Expand-Archivecmdlet z jednym z zestawu parametrów:

Expand-Archive -LiteralPath C:\source\file.Zip -DestinationPath C:\destination
Expand-Archive -Path file.Zip -DestinationPath C:\destination
Saleh Rahimzadeh
źródło
12

Hej, to działa dla mnie ..

$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
źródło
2
Jeśli jeden z plików lub katalogów już istnieje w miejscu docelowym, pojawia się okno dialogowe z pytaniem, co zrobić (zignorować, zastąpić), co nie pozwala na osiągnięcie celu. Czy ktoś wie, jak zmusić go do cichego zastąpienia?
Oleg Kazakov
Odpowiedź na komentarz @OlegKazakov: istnieje zestaw opcji kontrolujących CopyHeremetodę. Chyba @OlegKazakov już rozwiązał swój problem. Niemniej jednak umieszczam ten link tutaj dla innych internautów, którzy mogą znaleźć ten temat: docs.microsoft.com/en-us/previous-versions/windows/desktop/…
jsxt
4

Dla tych, którzy chcą korzystać z Shell.Application.Namespace.Folder.CopyHere () i chcą ukryć paski postępu podczas kopiowania lub skorzystać z większej liczby opcji, dokumentacja znajduje się tutaj:
https://docs.microsoft.com/en-us / windows / desktop / shell / folder-copyhere

Aby użyć programu PowerShell i ukryć paski postępu i wyłączyć potwierdzenia, możesz użyć następującego kodu:

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

Ograniczenia użycia Shell.Application w wersjach systemu Windows:
https://docs.microsoft.com/en-us/windows-server/administration/server-core/what-is-server-core

W podstawowych wersjach systemu Windows domyślnie pakiet powłoki Microsoft-Windows-Server-Shell nie jest zainstalowany, więc shell.applicaton nie będzie działać.

Uwaga : Wyodrębnianie archiwów w ten sposób zajmie dużo czasu i może spowolnić GUI systemu Windows

Matej Ridzon
źródło
3

Korzystanie z expand-archiveautomatycznego tworzenia katalogów nazwanych na podstawie archiwum:

function unzip ($file) {
    $dirname = (Get-Item $file).Basename
    New-Item -Force -ItemType directory -Path $dirname
    expand-archive $file -OutputPath $dirname -ShowProgress
}
mikemaccana
źródło
To koniecznie rozwija się w bieżącym katalogu, prawda?
jpmc26
Naprawdę nie widzę wartości dodanej automatycznego tworzenia. Bardziej elastyczne jest dodanie drugiego parametru, outputPathjak w zaakceptowanej odpowiedzi. W tym rozwiązaniu (jak powiedział jpmc26) zawsze utworzysz nowy katalog w bieżącym katalogu, więc możliwe jest, że musisz ustawić bieżący katalog zanim zadzwoniszunzip
Rubanov
Większość archiwizatorów wypakowuje do katalogu nazwanego na podstawie archiwum, w tym samym miejscu, co archiwum. Nic nie powstrzyma Cię przed dodaniem parametrów, jeśli chcesz czegoś innego, ale jest to rozsądne ustawienie domyślne.
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)
    }
}

Za pomocą:

unzip 'Applications\Site.zip' 'C:\inetpub\wwwroot\Site'
użytkownik1624251
źródło
Nie zapomnij się pozbyć, $archivea $archiveFilena końcu
tom.maruska
0

ForEachPętla przetwarza każdy plik ZIP znajdujący się w $filepathzmiennej

    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
źródło