How to create, update, and extract zip files with PowerShell
Lets take a look at working with zip files in PowerShell using .NET or the newer Archive module.
If you are going to be following along in PowerShell, here are examples of the variables I’m referring to with each snippet:
$zipFilePath = 'C:\Temp\zip.zip'
$toZipDirectory = 'C:\Temp\Zip'
$toZipDirectory2 = 'C:\Temp\Zip2'
$extractPath = 'C:\Temp\Extract'
$file1 = 'C:\Temp\File1.txt'
$file2 = 'C:\Temp\File2.txt'
$file3 = 'C:\Temp\File3.txt'
Pre PowerShell v5, .NET style
Before PowerShell v5, we had to rely on .NET to work with zip files. For these examples, I will be using .NET 4.5.
Before you begin, add the type to your session:
Add-Type -Assembly 'System.IO.Compression.FileSystem'
Create a .zip file
There are two notable ways to create .zip files with .NET. The first is from a directory using the CreateFromDirectory()
method. You give it the directory you want to zip, then the path of the .zip file that you want to create:
[System.IO.Compression.ZipFile]::CreateFromDirectory($toZipDirectory, $zipFilePath)
You can verify the contents using the OpenRead()
method:
[System.IO.Compression.ZipFile]::OpenRead($zipFilePath).Entries.Name
The other way I want to mention is by using the Open()
method. This method requires 2 parameters, the first is the zip file that you are ‘opening’ and the second is the ‘mode’ to open it with. It accepts ‘create’, ‘update’, and ‘read’. Don’t worry, we’ll check out all of them!
Here we are using the ‘create’ mode to create an empty .zip file:
$zip = [System.IO.Compression.ZipFile]::Open($zipFilePath, 'create')
And always make sure to close the locks on that file like so:
$zip.Dispose()
Update a .zip file
We can add files to a .zip file using .NET, but first we should define a compression level for the files:
$compressionLevel = [System.IO.Compression.CompressionLevel]::Fastest
We can specify: Fastest, NoCompression, or Optimal.
Then, lets open one of the .zip files for updating using the ‘update’ mode:
$zip = [System.IO.Compression.ZipFile]::Open($zipFilePath, 'update')
And then add a file to it:
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip, $file, (Split-Path $file1 -Leaf), $compressionLevel)
The third parameter in the CreateEntryFromFile()
method is asking for the display name of the .zip file and I’m using Split-Path
to pass the name of the file itself.
Bulk update a .zip file
We can, of course, bulk add files to a .zip file as well! This uses the same .zip file we opened for updating and the earlier defined compression level:
Get-ChildItem $FilesDirectory | ForEach-Object {[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip, $_.FullName, (Split-Path $_.FullName -Leaf), $compressionLevel)}
And don’t forget to close out that .zip file:
$zip.Dispose()
Extract a .zip file
To extract a .zip file using .NET, we must first open it for reading (told you we’d use all of the modes!):
$zip = [System.IO.Compression.ZipFile]::Open($zipFilePath, 'read')
And then we can use the ExtractToDirectory()
method, giving it the .zip file we just opened and the path to extract it to:
[System.IO.Compression.ZipFileExtensions]::ExtractToDirectory($zip, $extractPath)
To verify the extraction, we can use Get-ChildItem
:
Get-ChildItem $extractPath
To only extract a single file from a .zip file, things get a little trickier. We need to specify the ‘entry’ object within the .zip file object and pass that to the ExtractToFile()
method.
Here, we are extracting the first file in our .zip file by referencing the first item in the .Entries array, naming it ‘ExtractedFile1.txt’, and then specifying $true
for overwrite.
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($zip.Entries[0], "$extractPath\ExtractedFile1.txt", $true)
That gets tedious doing it for every file, so lets use the pipeline! We can filter using the Where-Object cmdlet. In this example, we are going to extract all .txt files from that .zip file:
$zip.Entries | Where-Object Name -like *.txt | ForEach-Object{[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$extractPath\$($_.Name)", $true)}
Or maybe we want all files that were modified in the last month:
$zip.Entries | Where-Object {$_.LastWriteTime.DateTime -gt (Get-Date).AddMonths(-1)} | ForEach-Object{[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$extractPath\$($_.Name)", $true)}
There is a lot of things you can do with that.
And don’t forget to close out the .zip file of course:
$zip.Dispose()
PowerShell v5, module style
In PowerShell v5, we have the Microsoft.PowerShell.Archive module that we can take advantage of! No more .NET! Well, no more overt .NET anyway.
Create a .zip file
We now have the convenience of using a nice easy Compress-Archive
cmdlet to create a .zip file either from a directory or from a file.
Using a directory:
Compress-Archive -Path $toZipDirectory -DestinationPath $zipFilePath
Using a file, or in this case, an array of files. Each fully qualified name separated by a comma:
Compress-Archive -Path $file1,$file2 -DestinationPath $zipFilePath
Update a .zip file
We use the same Compress-Archive
cmdlet to update a .zip file as well, but now we need to add the -Update
switch.
To update the .zip file with an additional directory:
Compress-Archive -Path $toZipDirectory2 -DestinationPath $zipFilePath -Update
Or to add in another file to the .zip file:
Compress-Archive -Path $file3 -DestinationPath $zipFilePath -Update
Extract a .zip file
To extract a .zip file, the archive module has the Expand-Archive
cmdlet. The only thing it needs is the path of the .zip file and the extraction path:
Expand-Archive -Path $zipFilePath -DestinationPath $extractPath
Unfortunately there is no way (that I know of) to use the Expand-Archive
cmdlet and specify only certain files for extraction.
Leave a Comment