11

I need to unzip a specific directory from a zipfile.

Like for example extract the directory 'test\etc\script' from zipfile 'c:\tmp\test.zip' and place it in c:\tmp\output\test\etc\script.

The code below works but has two quirks:

  • I need to recursively find the directory ('script') in the zip file (function finditem) although I already know the path ('c:\tmp\test.zip\test\etc\script')

  • With CopyHere I need to determine the targetdirectory, specifically the 'test\etc' part manually

Any better solutions? Thanks.

The code:

function finditem($items, $itemname)
{
  foreach($item In $items)
  {
    if ($item.GetFolder -ne $Null)
    {
      finditem $item.GetFolder.items() $itemname
    }
    if ($item.name -Like $itemname)
    {
        return $item
    } 
  } 
} 

$source = 'c:\tmp\test.zip'
$target = 'c:\tmp\output'

$shell = new-object -com shell.application

# find script folder e.g. c:\tmp\test.zip\test\etc\script
$item = finditem $shell.NameSpace($source).Items() "script"

# output folder is c:\tmp\output\test\etc
$targetfolder = Join-Path $target ((split-path $item.path -Parent) -replace '^.*zip')
New-Item $targetfolder -ItemType directory -ErrorAction Ignore

# unzip c:\tmp\test.zip\test\etc\script to c:\tmp\output\test\etc
$shell.NameSpace($targetfolder).CopyHere($item)
Simon de Kraa
  • 415
  • 2
  • 6
  • 18
  • I know the location so it looks like a shame to have to recursively find the directory. The .NET 4.5 code works but I am reluctant to add another prerequisite. – Simon de Kraa Jul 10 '14 at 14:58
  • All right, then it is possible to avoid the search and another prerequisite, see my answer. – Roman Kuzmin Jul 10 '14 at 16:33

3 Answers3

14

I don't know about most elegant, but with .Net 4.5 installed you could use the ZipFile class from the System.IO.Compression namespace:

[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null

$zipfile = 'C:\path\to\your.zip'
$folder  = 'folder\inside\zipfile'
$dst     = 'C:\output\folder'

[IO.Compression.ZipFile]::OpenRead($zipfile).Entries | ? {
  $_.FullName -like "$($folder -replace '\\','/')/*"
} | % {
  $file   = Join-Path $dst $_.FullName
  $parent = Split-Path -Parent $file
  if (-not (Test-Path -LiteralPath $parent)) {
    New-Item -Path $parent -Type Directory | Out-Null
  }
  [IO.Compression.ZipFileExtensions]::ExtractToFile($_, $file, $true)
}

The 3rd parameter of ExtractToFile() can be omitted. If present it defines whether existing files will be overwritten or not.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
11

As far as the folder location in a zip is known, the original code can be simplified:

$source = 'c:\tmp\test.zip'  # zip file
$target = 'c:\tmp\output'    # target root
$folder = 'test\etc\script'  # path in the zip

$shell = New-Object -ComObject Shell.Application

# find script folder e.g. c:\tmp\test.zip\test\etc\script
$item = $shell.NameSpace("$source\$folder")

# actual destination directory
$path = Split-Path (Join-Path $target $folder)
if (!(Test-Path $path)) {$null = mkdir $path}

# unzip c:\tmp\test.zip\test\etc\script to c:\tmp\output\test\etc\script
$shell.NameSpace($path).CopyHere($item)
Roman Kuzmin
  • 40,627
  • 11
  • 95
  • 117
  • This is slower than ExtractToFile – Mike Nov 03 '14 at 15:07
  • 3
    Just so someone else doesn't run into my same issue. When trying to access into a nupkg file using this you have to rename the file to be a .zip in order for it to be interpreted correctly. – SteckDEV Aug 26 '15 at 18:44
1

Windows PowerShell 5.0 (included in Windows 10) natively supports extracting ZIP files using Expand-Archive cmdlet:

Expand-Archive -Path Draft.Zip -DestinationPath C:\Reference
Ivan Zhakov
  • 3,981
  • 28
  • 24