ChatGPT解决这个技术问题 Extra ChatGPT

How to unzip a file in Powershell?

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.

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.
Since Expand-Archive now exists in powershell, the accepted answer is a bit out of date.

L
Lam Le

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

Expand-Archive c:\a.zip -DestinationPath c:\a

Use $PSVersionTable.PSVersion to determine what version of PowerShell you are running.
@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.
Looks like the parameter OutputPath has been changed to DestinationPath (reference msdn.microsoft.com/powershell/reference/5.1/…)
You can also use relative paths like Expand-Archive -Path .\a.zip -DestinationPath .
Note the Expand-Archive and Compress-Archive require that archive file extension is .zip, while [System.IO.Compression.ZipFile]-based solutions don't.
L
Lekensteyn

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:

Existing files will not be overwritten and instead trigger an IOException.

This method requires at least .NET Framework 4.5, available for Windows Vista and newer.

Relative paths are not resolved based on the current working directory, see Why don't .NET objects in PowerShell use the current directory?

See also:

How to Compress and Extract files (Microsoft Docs)


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. :-)
Why do you create a function to replace a single function call?
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.
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
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.
I
Igor Popov

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


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.
S
Saleh Rahimzadeh

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

A
Abhijit

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

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?
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: docs.microsoft.com/en-us/previous-versions/windows/desktop/…
K
Kellen Stuart

Use the built in powershell method Expand-Archive

Example

Expand-Archive -LiteralPath C:\archive.zip -DestinationPath C:\

m
mikemaccana

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
}

This necessarily expands in the current directory, doesn't it?
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
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.
M
Matej Ridzon

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://docs.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://docs.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


u
user1624251
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'

Don't forget to dispose $archive and $archiveFile at the end
T
THess

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
    }