Share via


Is it possible to using Powershell, Unzipping, force 'yes' to all, rename, don't overwrite existing files.

Question

Monday, July 14, 2014 8:11 PM

I've been reading and experimenting with a number for forums, solutions, and technet articles. What I haven't found yet is a method/mode to allow me to unzip an archive that has multiple files with the same filename.

I have system generated zip files that have multiple files that have the same filename. They are indeed different files though.

Some folks have provided some really great resources and examples of how to use system.io.compression, but I haven't found the answer to my problem.

I 'think' if it were possible, I would find it here.

http://msdn.microsoft.com/en-us/library/system.io.compression.zipfile_methods%28v=vs.110%29.aspx

I found this article that makes it seem like I should switch to using the cli version of 7zip.

http://social.technet.microsoft.com/Forums/windowsserver/en-US/8f98f06e-79fe-401c-92f0-a548d4304dcc/how-to-zip-files-and-folders-using-powershell?forum=winserverpowershell

Ultimately, my goal via powershell is to:

unzip the file from in a PS script

Make it non user interactive.

Have it answer 'Y' to any prompts that may be issued

rename all duplicate filename's automatically just as in a manual extraction.

It would seem that if the functionality exists in the OS, .NET 4.5 would expose it to PS 3.

The closest hint to how to do this is using vOption as referenced in this page:

http://www.howtogeek.com/tips/how-to-extract-zip-files-using-powershell/

When I tried that it did some really amazing things, just not 'good' amazing things. (That example showed for java I believe)

Thanks for any ideas, especially links to better documentation that allows me to see how this is done.

-= Bruce D. Meyer

All replies (13)

Wednesday, July 16, 2014 4:41 AM ✅Answered | 2 votes

So there are probably much more efficient ways to do this, but this seems to be working for me now:

Add-Type -AssemblyName System.IO.Compression.FileSystem

$extractDir = 'C:\Scripting\Testing\7-16-2014\TEST'

$zip = [System.IO.Compression.ZipFile]::OpenRead('C:\Scripting\Testing\7-16-2014\TEST\file.zip')

foreach ($item in $zip.Entries) {

    try {

        [System.IO.Compression.ZipFileExtensions]::ExtractToFile($item,(Join-Path -Path $extractDir -ChildPath $item.FullName),$false)

    } catch {

        $count = 0
        $extension = $item.FullName.Substring($item.FullName.LastIndexOf('.'))
        $fileName = $item.FullName.Substring(0,$item.FullName.LastIndexOf('.'))
        $found = $false

        do {
        
            if (!(Test-Path (Join-Path -Path $extractDir -ChildPath "$fileName ($count)$extension"))) {
            
                $found = $true
            
            } else {

                $count++

            }
        
        } until ($found -eq $true)

       $newFileName = "$fileName ($count)$extension"

       [System.IO.Compression.ZipFileExtensions]::ExtractToFile($item,(Join-Path -Path $extractDir -ChildPath $newFileName),$false)

    }

}

Don't retire TechNet! - (Don't give up yet - 12,950+ strong and growing)


Wednesday, July 16, 2014 3:26 PM ✅Answered | 1 vote

Hi Bruce,

Here's the same code with some comments to explain what's going on:

# Load the required assembly.
Add-Type -AssemblyName System.IO.Compression.FileSystem

# Set the directory to extract the zip file to.
$extractDir = 'C:\Scripting\Testing\7-16-2014\TEST'

# Open the specified zip file.
$zip = [System.IO.Compression.ZipFile]::OpenRead('C:\Scripting\Testing\7-16-2014\TEST\file.zip')

# Loop through each item contained in the zip file.
foreach ($item in $zip.Entries) {

    # Attempt to unzip the file. If a file with the same name already exists, jump to the catch block.
    try {

        [System.IO.Compression.ZipFileExtensions]::ExtractToFile($item,(Join-Path -Path $extractDir -ChildPath $item.FullName),$false)

    } catch {

        # If we're here, that means that a file already exists with the same name as the file that's being unzipped.
        # To work around this, we'll figure out what number can be appended to the name to make it unique.
        # The new file name structure I'm using is <filename><space><open paren><number><close paren><dot><extension>.
        # You can change this if desired.

        # Set a counter for file naming.
        $count = 0
        # Get the extension of the file that is being unzipped.
        $extension = $item.FullName.Substring($item.FullName.LastIndexOf('.'))
        # Get the name of the file that is being unzipped.
        $fileName = $item.FullName.Substring(0,$item.FullName.LastIndexOf('.'))
        # Testing flag.
        $found = $false

        # Loop until the $found flag is set to $true.
        do {
        
            # Test if a file already exists with the new file name, based on the value currently in $count.
            # 
            if (!(Test-Path (Join-Path -Path $extractDir -ChildPath "$fileName ($count)$extension"))) {
            
                # File doesn't exist, so set the $found flag to $true.
                $found = $true
            
            } else {

                # File does exist, so increment $count and try again.
                $count++

            }
        
        } until ($found -eq $true)

        # Set variable with the new file name to use.
        $newFileName = "$fileName ($count)$extension"

        # Unzip the file with the new name.
        [System.IO.Compression.ZipFileExtensions]::ExtractToFile($item,(Join-Path -Path $extractDir -ChildPath $newFileName),$false)

    }

}

Also, you're very welcome. This was a good exercise for me to learn a few new things, as I've never actually used the ZipFileExtensions class until now.

Don't retire TechNet! - (Don't give up yet - 12,950+ strong and growing)


Monday, July 14, 2014 8:41 PM

Have you tried unzipping a file using this?

[System.IO.Compression.ZipFile]::ExtractToDirectory("C:\temp\zipfile.zip","C:\temp\extractdir")

EDIT: Oh, and that assembly isn't loaded into Powershell by default - you need to do this first:

Add-Type -AssemblyName System.IO.Compression.Filesystem

Monday, July 14, 2014 8:45 PM

Yes. It gives me this error:

WARNING: Unexpected Error. Error details: Exception calling "ExtractToDirectory" with "2" argument(s): "The file 'H:\Images\Windows\test_12345\mir.urlhistory.xml' already exists.".Except
ion.Message

Monday, July 14, 2014 8:52 PM

Are you extracting a single zip file to that directory, or more than one? I'd imagine that the ZipFile class retains folder structure when extracting an archive, so receiving that error seems a bit odd to me - an archive, as far as I know, follows the same rules as the file system, which means that a full file path must be unique.


Monday, July 14, 2014 9:00 PM

I've attached a screenshot. It is a single zip file. Inside the zip file four of the files shown have the exact same name. The way I have handled this prior to attempting to script it, is to select 'Keep Both Copies, rename, repeat for the rest.,' (Second attachments shows the options I use with windows explorer)

It is a single archive


Tuesday, July 15, 2014 3:08 AM

Hi,

It looks like you might have to stick with Shell.Application to force the automatic renaming. Try using 24 as the vOptions (8 + 16):

$shell = New-Object -ComObject Shell.Application
$zip = $shell.NameSpace('C:\Scripting\Testing\7-14-2014\TEST\file.zip')

foreach ($item in $zip) {

    $shell.Namespace('C:\Scripting\Testing\7-14-2014\TEST').CopyHere($item,24)

}

This adjustment of the code you linked to above worked for me in my testing, but I wasn't actually able to create a zip file with multiple files of the same name inside of it for testing purposes.

One thing to note though - Shell.Application doesn't always (or often) play well with being scheduled.

Don't retire TechNet! - (Don't give up yet - 12,950+ strong and growing)


Tuesday, July 15, 2014 4:27 PM

I tried adapting your example to my code. and it kept making a copy of the zip file.

test_12345 - Copy.zip

test_12345.zip

I then copied and pasted your example, and deleted all preceding code so the entire prgram is nothing more than your example to rule out any of my previous stuff. I then replaced the path/name with my own with the same results.

At least I am learning quite a bit from the trial and error.

My first time through was trying to do this without using a foreach loop which at least extracted the files. The closes I cam so far was extracting the files, and popping up an offer to rename and do the same for the remaining items.

Below is how I tried to write it without a foreach which gave the same results.

Obviously I am a n00b with PS.

-= Bruce

# Get the case name which will be used for the folder names.
$caseName = Read-Host "Enter Case Name for directory to parse [Press Enter]"
$caseDrive = "H:\Images\Windows\+$caseName
if(!(Test-Path -Path $caseName))
        {
 Write-Host -ForegroundColor Green "`n`nDirectory name $caseName exists, Good to go."    
        }
    else
        {
    Write-Host -ForegroundColor Red "`n`nDirectory name $caseName does not exist, back-up and punt."
    exit;
        }
 Write-Host   "Renaming $caseDrive\_Audit_Script_*.zip $caseDrive\caseName.zip"
 Move-Item $caseDrive\_Audit_Script_*.zip $caseDrive\caseName.zip
 $caseZip = "$caseDrive\caseName.zip"
 Write-Host   "Unzipping: $caseZip"
 
 # UnZipper($file, $destination)
 # Unzip-File -File $caseDrive\caseName.zip -Destination $caseDrive
 $shell.Namespace($caseDrive).CopyHere($caseZip,24)
 Write-Host -ForegroundColor Yellow " Unzipping complete.`n"


Tuesday, July 15, 2014 5:39 PM

Give this basic tester a try and see if it works on your test zip:

$zipFile = 'C:\Path\To\ZipFile.zip'
$extractTo = 'C:\Path\To\Extract\To'

$shell = New-Object -ComObject Shell.Application
$zip = $shell.NameSpace($zipFile)

foreach ($item in $zip) {

    $shell.Namespace($extractTo).CopyHere($item,24)

}

Don't retire TechNet! - (Don't give up yet - 12,950+ strong and growing)


Tuesday, July 15, 2014 8:11 PM

This was interesting. The first time I ran it, no errors, nothing happened.

So I changed the extract to dir to a different one. What happened is it copies the zip file (without extracting) to the unique extract dir.

Here is what I had, and changed:

$zipFile = 'H:\Images\Windows\test_12345\test_12345.zip'

# Original
# $extractTo = 'H:\Images\Windows\test_12345'

# Changed to:
$extractTo = 'H:\Images\Windows\testout'

$shell = New-Object -ComObject Shell.Application
$zip = $shell.NameSpace($zipFile)

foreach ($item in $zip) {

    $shell.Namespace($extractTo).CopyHere($item,24)

}

Tuesday, July 15, 2014 8:14 PM

If this helps any, here is the 7za -l structure of the archive I am using:

PS H:\Images\Windows\test_12345> 7za l test_12345.zip

7-Zip (A) 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18

Listing archive: test_12345.zip

--
Path = test_12345.zip
Type = zip
Physical Size = 57314008

   Date      Time    Attr         Size   Compressed  Name
     
1980-01-01 00:00:00 .....         4777         1598  mir.w32system.xml
1980-01-01 00:00:00 .....         2430          793  mir.w32volumes.xml
1980-01-01 00:00:00 .....          482          321  Module Issues - issues.mir.w32volumes.xml
1980-01-01 00:00:00 .....         4950          925  mir.w32network-dns.xml
1980-01-01 00:00:00 .....        20162         2360  mir.w32ports.xml
1980-01-01 00:00:00 .....         1840          521  mir.w32disks.xml
1980-01-01 00:00:00 .....          489          322  Module Issues - issues.mir.w32disks.xml
1980-01-01 00:00:00 .....         6111         1185  mir.w32useraccounts.xml
1980-01-01 00:00:00 .....         8951          976  Module Issues - issues.mir.w32useraccounts.xml
1980-01-01 00:00:00 .....        18634         2220  mir.w32network-arp.xml
1980-01-01 00:00:00 .....        18956         1886  mir.w32network-route.xml
1980-01-01 00:00:00 .....      1504797       135194  mir.w32prefetch.xml
1980-01-01 00:00:00 .....     16230400      1672007  mir.w32scripting-persistence.xml
1980-01-01 00:00:00 .....       256105        12403  Module Issues - issues.mir.w32scripting-persistence.xml
1980-01-01 00:00:00 .....    444456795     25271008  mir.w32eventlogs.xml
1980-01-01 00:00:00 .....        42300         3045  Module Issues - issues.mir.w32eventlogs.xml
1980-01-01 00:00:00 .....       133391        14621  mir.w32tasks.xml
1980-01-01 00:00:00 .....         2913          673  Module Issues - issues.mir.w32tasks.xml
1980-01-01 00:00:00 .....       317812        44329  mir.w32services.xml
1980-01-01 00:00:00 .....        32604         1472  C__Windows_Tasks_SCHEDLGU.TXT
1980-01-01 00:00:00 .....     11469940       471392  C__WINDOWS_INF_SETUPAPI.APP.LOG
1980-01-01 00:00:00 .....      1699695       144530  mir.w32apifiles.xml
1980-01-01 00:00:00 .....        32077         3831  Module Issues - issues.mir.w32apifiles.xml
1980-01-01 00:00:00 .....         1131          593  C__Windows_SYSTEM32_DRIVERS_ETC_HOSTS
1980-01-01 00:00:00 .....          334          193  mir.w32systemrestore.xml
1980-01-01 00:00:00 .....          432          273  Module Issues - issues.mir.w32systemrestore.xml
1980-01-01 00:00:00 .....       786432       151107  C__Users_admin_NTUSER.DAT
1980-01-01 00:00:00 .....      1048576       183146  C__Users_Administrator_NTUSER.DAT
1980-01-01 00:00:00 .....      9437184      1981741  C__Users_bdmeyer_NTUSER.DAT
1980-01-01 00:00:00 .....       262144        46842  C__Users_Default_NTUSER.DAT
1980-01-01 00:00:00 .....      2621440       559772  C__Users_LocalAdmin_NTUSER.DAT
1980-01-01 00:00:00 .....       262144          600  C__Users_Public_ntuser.dat
1980-01-01 00:00:00 .....       262144          599  C__Users_TEMP_ntuser.dat
1980-01-01 00:00:00 .....       262144        18774  C__Windows_System32_config_SAM
1980-01-01 00:00:00 .....       262144         6572  C__Windows_System32_config_SECURITY
1980-01-01 00:00:00 .....     93061120     20381660  C__Windows_System32_config_SOFTWARE
1980-01-01 00:00:00 .....     22020096      4912283  C__Windows_System32_config_SYSTEM
1980-01-01 00:00:00 .....      3057294       233720  mir.filedownloadhistory.xml
1980-01-01 00:00:00 .....        12756          975  Module Issues - issues.mir.filedownloadhistory.xml
1980-01-01 00:00:00 .....      1645822       174081  mir.cookiehistory.xml
1980-01-01 00:00:00 .....        10784          800  Module Issues - issues.mir.cookiehistory.xml
1980-01-01 00:00:00 .....        10942         1711  mir.formhistory.xml
1980-01-01 00:00:00 .....        11179          833  Module Issues - issues.mir.formhistory.xml
1980-01-01 00:00:00 .....        43965         4389  mir.urlhistory.xml
1980-01-01 00:00:00 .....         1626          405  Module Issues - issues.mir.urlhistory.xml
1980-01-01 00:00:00 .....      9882131       845406  mir.urlhistory.xml
1980-01-01 00:00:00 .....         1970          405  Module Issues - issues.mir.urlhistory.xml
1980-01-01 00:00:00 .....        14400         2918  mir.urlhistory.xml
1980-01-01 00:00:00 .....         3545          484  Module Issues - issues.mir.urlhistory.xml
1980-01-01 00:00:00 .....          319          190  mir.urlhistory.xml
1980-01-01 00:00:00 .....         7026          736  Module Issues - issues.mir.urlhistory.xml
1980-01-01 00:00:00 .....       106048         7148  bdmeyer-win7u64_ Acquisition with script DSIT_Audit_Script for bdmeyer-win7u64 - batchresults.xml
     
                             621363883     57305968  52 files, 0 folders

You'll see there are 4 files name  mir.urlhistory.xml


Tuesday, July 15, 2014 8:34 PM

So I changed the extract to dir to a different one. What happened is it copies the zip file (without extracting) to the unique extract dir.

Yep, you're right about this. I missed .Items() on $zip in the foreach loop.

Seems like I'm still getting prompted to overwrite after making that adjustment, I'm poking at it to see if I can make it actually work properly now.

EDIT: Perhaps this isn't going to work, no matter what flags are set:

http://mattclingan.wordpress.com/2007/08/08/unzip-and-the-copyhere-command-with-vuseless-voptions/

0x8 works just fine. 0x10 doesn't...

Don't retire TechNet! - (Don't give up yet - 12,950+ strong and growing)


Wednesday, July 16, 2014 1:24 PM

Wow. That works.

I don't understand what is happening. Would you mind explaining what your code is doing?

I have some Get-Help exploring to do.

Thank you for your tireless help with this Sir. Is this a problem with something in powershell not exposing .net properly that makes this such an arduous task?

-= Bruce