315

I have a .zip file and need to unpack its entire content using Powershell. I'm doing this but it doesn't seem to work:

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

What's wrong? The directory C:\a is still empty.

Uli Kunkel
  • 3,485
  • 3
  • 12
  • 8
  • 7
    If you're in Powershell 2.0, or without .NET 4.5 installed, then the method you mentioned is the only path (without going with a 3rd-party exe (i.e. 7zip). I would say that the question isn't fully answered until someone provides why this method doesn't work. It does for me some of the time, but others it doesn't. – kenny Jan 06 '16 at 17:32
  • 3
    Since `Expand-Archive` now exists in powershell, the accepted answer is a bit out of date. – Kellen Stuart Aug 11 '21 at 16:59

10 Answers10

671

In PowerShell v5+, there is an Expand-Archive command (as well as Compress-Archive) built in:

Expand-Archive C:\a.zip -DestinationPath C:\a
Josh Correia
  • 3,807
  • 3
  • 33
  • 50
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • 38
    Use `$PSVersionTable.PSVersion` to determine what version of PowerShell you are running. – Brad C Mar 10 '16 at 18:37
  • 1
    @LoneCoder I don't think you can blame Ballmer. Windows has *never* had a built in command line tool for handling compressed files before, even though gzip came out in 1992 and tar is even older. – jpmc26 Mar 10 '16 at 22:14
  • 2
    @Ghashange PowerShell 5 wasn't even available for anything below Windows 10 and Server 2012 when this answer was posted, even as a pre-release. – jpmc26 Mar 10 '16 at 22:15
  • 12
    Looks like the parameter `OutputPath` has been changed to `DestinationPath` (reference https://msdn.microsoft.com/powershell/reference/5.1/microsoft.powershell.archive/Expand-Archive) – Elijah W. Gagne Nov 15 '16 at 13:54
  • 3
    You can also use relative paths like `Expand-Archive -Path .\a.zip -DestinationPath .` – Culip Oct 19 '18 at 13:48
  • Warning: Expand-Archive is much slower than 7zip. See https://github.com/PowerShell/Microsoft.PowerShell.Archive/issues/32 – Lekensteyn Sep 08 '19 at 22:06
  • 3
    Note the `Expand-Archive` and `Compress-Archive` require that archive file extension is `.zip`, while `[System.IO.Compression.ZipFile]`-based solutions don't. – Jonathan May 07 '20 at 13:08
  • At least in 5.1 simply **Expand-Archive .\my.zip newOutputFolder** to unzip to new subfolder of current directory – Slashback Feb 12 '22 at 01:23
  • FYI for future readers. Expand-Archive will create the complete DestinationPath if it doesn't exist. – Dr Phil May 11 '23 at 19:15
  • @Jonathan in newer versions of PowerShell `Expand-Archive` [with its `-PassThru` option] outputs the files it's expanding and is useful for further processing down the pipeline, which .NET's `ZipFile` can't automatically do :( – Hashbrown Aug 29 '23 at 09:26
290

Here is a simple way using ExtractToDirectory from 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"

Note that if the target folder doesn't exist, ExtractToDirectory will create it. Other caveats:

See also:

Lekensteyn
  • 64,486
  • 22
  • 159
  • 192
Micky Balladelli
  • 9,781
  • 2
  • 33
  • 31
  • 1
    There is no need to quote the parameters C:\a.zip or C:\a unless there is a space in the path. PowerShell considers parameters to be of type string or numeric unless the argument starts with a special character like $, @, ( or {. This is one of benefits of PowerShell being a `shell` scripting language. :-) – Keith Hill Jan 04 '15 at 22:35
  • 11
    Why do you create a function to replace a single function call? –  Aug 01 '15 at 08:32
  • 19
    In theory you don't. I try to hide complex/unconventional calls in functions so later I can replace the method without worrying about where it's used. As Keith mentioned, in V5 there will be a new way to do it. – Micky Balladelli Aug 01 '15 at 09:37
  • 1
    You need at least .NET Framework 4.5 for this. See the bottom of https://msdn.microsoft.com/en-us/library/system.io.compression.zipfile(v=vs.110).aspx – ferventcoder Dec 14 '16 at 21:33
  • 10
    I tried this but getting below error, `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 Dec 23 '16 at 04:36
  • 5
    This gives me the following error: `Add-Type : Cannot add type. The assembly 'System.IO.Compression.FileSystem' could not be found.`. I have .NET 4.6.2 installed, and I've verified that the assembly is in the GAC, but I didn't figure out why I'm getting this error. – Sam Jan 05 '17 at 04:53
  • I misclicked a downvote instead of an upvote, sorry about that. – imps Jul 11 '18 at 23:16
  • 1
    One serious limitation of this approach (when it works) is that destination directory cannot already exist. If it does, you will get an exception and there is no way to force it overwrite existing files either. – Oleg Kazakov Oct 29 '18 at 04:31
  • @MickyBalladelli why does this not run in the current location? I tried modifying your code to accept a location and it calls Set-Location before trying to extract but its just looking for the zip on my local computer – SketchyTurtle Apr 02 '19 at 20:42
36

In PowerShell v5.1 this is slightly different compared to v5. According to MS documentation, it has to have a -Path parameter to specify the archive file path.

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

Or else, this can be an actual path:

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

Expand-Archive Doc

Igor Popov
  • 9,795
  • 7
  • 55
  • 68
NIK
  • 1,089
  • 13
  • 22
  • 5
    There is no difference between v5 and v5.1 in this Cmdlet. You do not need to name the first parameter; it will automatically become the path. For example, `Expand-Archive Draft.Zip -DestinationPath C:\Reference` works without issue. In addition, it's not *actual* path, but *absolute* path. – Franklin Yu Sep 13 '18 at 14:29
28

Use Expand-Archive cmdlet with one of parameter set:

Expand-Archive -LiteralPath C:\source\file.Zip -DestinationPath C:\destination
Expand-Archive -Path file.Zip -DestinationPath C:\destination
Saleh Rahimzadeh
  • 1,261
  • 12
  • 10
14

Hey Its working for me..

$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
  • 333
  • 4
  • 7
  • 2
    If one of the files or directories already exists at the destination location, it pops up a dialogue asking what to do (ignore, overwrite) which defeats the purpose. Does anyone know how to force it to silently overwrite? – Oleg Kazakov Oct 29 '18 at 04:56
  • Answering to the comment by @OlegKazakov: there is set of option controlling the `CopyHere` method. I guess @OlegKazakov already solved his issue. Nevertheless I put this link here for other surfers who can find this topic: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/sidebar/system-shell-folder-copyhere – jsxt Aug 09 '19 at 15:58
12

Use the built in powershell method Expand-Archive

Example

Expand-Archive -LiteralPath C:\archive.zip -DestinationPath C:\
Kellen Stuart
  • 7,775
  • 7
  • 59
  • 82
5

Using expand-archive but auto-creating directories named after the archive:

function unzip ($file) {
    $dirname = (Get-Item $file).Basename
    New-Item -Force -ItemType directory -Path $dirname
    expand-archive $file -OutputPath $dirname -ShowProgress
}
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
  • This necessarily expands in the current directory, doesn't it? – jpmc26 Mar 21 '17 at 20:59
  • Don't really see the added value of auto creating. It's more flexible to add a second parameter `outputPath` like in the accepted answer. In this solution (as jpmc26 said), you will always create a new directory in the current directory so it's possible you need to set the current directory before you call `unzip` – MonkeyDreamzzz Jul 03 '17 at 12:29
  • 2
    Most archivers extract into a dir named after the archive, in the same place as the archive. Nothing to stop you adding parameters if you want something different, but it's a sensible default. – mikemaccana Jul 06 '17 at 13:07
5

For those, who want to use Shell.Application.Namespace.Folder.CopyHere() and want to hide progress bars while copying, or use more options, the documentation is here:
https://learn.microsoft.com/en-us/windows/desktop/shell/folder-copyhere

To use powershell and hide progress bars and disable confirmations you can use code like this:

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

Limitations of use of Shell.Application on windows core versions:
https://learn.microsoft.com/en-us/windows-server/administration/server-core/what-is-server-core

On windows core versions, by default the Microsoft-Windows-Server-Shell-Package is not installed, so shell.applicaton will not work.

note: Extracting archives this way will take a long time and can slow down windows gui

Matej Ridzon
  • 86
  • 1
  • 3
2
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)
    }
}

Using:

unzip 'Applications\Site.zip' 'C:\inetpub\wwwroot\Site'
user1624251
  • 119
  • 1
  • 3
1

ForEach Loop processes each ZIP file located within the $filepath variable

    foreach($file in $filepath)
    {
        $zip = $shell.NameSpace($file.FullName)
        foreach($item in $zip.items())
        {
            $shell.Namespace($file.DirectoryName).copyhere($item)
        }
        Remove-Item $file.FullName
    }
THess
  • 1,003
  • 1
  • 13
  • 21
Pradyumna
  • 11
  • 1