Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most elegant way to extract a directory from a zipfile using PowerShell?

Tags:

powershell

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)
like image 592
Simon de Kraa Avatar asked Jul 10 '14 09:07

Simon de Kraa


3 Answers

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)
like image 113
Roman Kuzmin Avatar answered Sep 27 '22 20:09

Roman Kuzmin


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.

like image 34
Ansgar Wiechers Avatar answered Sep 27 '22 21:09

Ansgar Wiechers


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
like image 36
Ivan Zhakov Avatar answered Sep 27 '22 21:09

Ivan Zhakov