I'm having a challenging time getting compress-archive
to do what I want...
I have a root project folder and I want to zip up some of the files in sub-directories and preserve the relative paths. For example:
/
├── _scripts
├── ├─_module1
| | └── filex.js
| └─_module2
| ├── file1.js
| └── file2.txt
So from my root directory, i'd like to create a zip file that includes module2/*
, and i want to keep the folder structure. I'd like my zip to contain:
scripts/module2/file1.js
scripts/module2/file2.txt
But when I run this from the root folder:
Compress-Archive -Path "scripts\module2\*" -DestinationPath tmp.zip
The contents of the zip file only contain:
/file1.js
/file2.txt
It appears that Compress-Archive
(as of Windows PowerShell v5.1) doesn't support what you want:
Targeting a folder recursively adds that folder's subtree to the archive, but only by the target folder's name (which becomes a child folder inside the archive), not its path.
Specifically,
Compress-Archive -Path scripts\module2 -DestinationPath tmp.zip
will (recursively) store the contents of scripts\module2
in tmp.zip
, but not with archive-internal path .\scripts\module2
, just with .\module2
- the target folder's name (the last input path component).
The implication is that you'd have to pass folder scripts
instead to get the desired archive-internal path, but that would invariably include the entire subtree of scripts
, given that Compress-Archive
offers no inclusion/exclusion mechanism.
One - cumbersome - option is to recreate the desired hierarchy in, say, the $env:TEMP
folder, copy the target folder there, run Compress-Archive
against the root of the recreated hierarchy, and then clean up:
New-Item -Force -ItemType Directory $env:TEMP/scripts
Copy-Item -Recurse -Force scripts/module2 $env:TEMP/scripts
Compress-Archive -LiteralPath $env:TEMP/scripts -DestinationPath tmp.zip
Remove-Item $env:TEMP/Scripts -Recurse -Whatif
Otherwise, you may be able to find a solution:
by using the .NET v4.5+ [System.IO.Compression.ZipFile]
class directly; you can load it into your session with Add-Type -Assembly System.IO.Compression.FileSystem
(not necessary in PowerShell Core).
by using external programs such as 7-Zip,
I wanted to do this without having to copy the full structure to a temp directory.
#build list of files to compress
$files = @(Get-ChildItem -Path .\procedimentos -Recurse | Where-Object -Property Name -EQ procedimentos.xlsx);
$files += @(Get-ChildItem -Path .\procedimentos -Recurse | Where-Object -Property Name -CLike procedimento_*_fs_*_d_*.xml);
$files += @(Get-ChildItem -Path .\procedimentos -Recurse | Where-Object -Property FullName -CLike *\documentos_*_fs_*_d_*);
# exclude directory entries and generate fullpath list
$filesFullPath = $files | Where-Object -Property Attributes -CContains Archive | ForEach-Object -Process {Write-Output -InputObject $_.FullName}
#create zip file
$zipFileName = 'procedimentos.zip'
$zip = [System.IO.Compression.ZipFile]::Open((Join-Path -Path $(Resolve-Path -Path ".") -ChildPath $zipFileName), [System.IO.Compression.ZipArchiveMode]::Create)
#write entries with relative paths as names
foreach ($fname in $filesFullPath) {
$rname = $(Resolve-Path -Path $fname -Relative) -replace '\.\\',''
echo $rname
$zentry = $zip.CreateEntry($rname)
$zentryWriter = New-Object -TypeName System.IO.BinaryWriter $zentry.Open()
$zentryWriter.Write([System.IO.File]::ReadAllBytes($fname))
$zentryWriter.Flush()
$zentryWriter.Close()
}
# clean up
Get-Variable -exclude Runspace | Where-Object {$_.Value -is [System.IDisposable]} | Foreach-Object {$_.Value.Dispose(); Remove-Variable $_.Name};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With