
close
close
Upcoming FREE Conference on Identity Management and Privileged Access Management
In a previous article I demonstrated how to use the new Compress-Archive cmdlet in PowerShell 5.0. In that article, I needed to create a zip file of selected files and retain the folder structure. In my original command I wanted to zip up XML files that reside in a number of subfolders under C:\Work. How can I retain that information?
I couldn’t find a simple way by using the cmdlet’s parameters or basic pipeline expressions. I have a solution, but it takes a little more effort.
Because I know the only way to maintain the folder structure is to only specify a folder name if I copy the files to a new directory structure and zip that up, then I should end up with a structured archive. Here’s how I accomplished this using the same XML files.
First, I’m going to get all the files I want and group them by their directory name. I’m sorting the results on the directory name so that when I create the corresponding temp folders, parent folders will get created first, although in reality, PowerShell doesn’t really care.
$g = dir c:\work\*.xml -Recurse | Group Directory | Sort name
The rest of the code looks like this:
$g | foreach -begin { #define the name of the zip file $zip = "c:\work\xmldata.zip" #delete the zip file if an old copy exists if (Test-Path $zip) { Del $zip } #create a temp folder $zipTemp = Join-Path -path $env:temp -childpath $([System.IO.Path]::GetRandomFileName()) write-host $zipTemp -ForegroundColor Yellow New-Item -Path $zipTemp -ItemType Directory -ea stop } -process { #get everything after drive:\ $folder = $_.name.Split(":")[1].Substring(1) #construct a new path $newpath = Join-Path -Path $zipTemp -ChildPath $folder write-host $newpath -ForegroundColor green #create a temp folder $tempdestination = New-Item -Path $newpath -ItemType directory #copy files to the folders $_.group | copy -Destination $tempdestination } -end { #compress each temp folder starting with the top level $top = dir $ziptemp -Directory Compress-Archive -Path $top.fullname -DestinationPath $zip #delete temp remove-item $ziptemp -Recurse -Force }
I’m using ForEach-Object in a way you may not be familiar with. I’m piping each group object to ForEach, but before I process anything, I delete my target zip file if it already exists and create a random folder name in the TEMP directory.
$zipTemp = Join-Path -path $env:temp -childpath $([System.IO.Path]::GetRandomFileName()) write-host $zipTemp -ForegroundColor Yellow New-Item -Path $zipTemp -ItemType Directory -ea stop
Then for each group object I split apart the name, which is the directory name, getting everything after C: because my top-level folder is C:\Work.
$folder = $_.name.Split(":")[1].Substring(1)
If it was something nested I’d need a bit more code to get just the top-level part, probably something with regular expressions. Once I have the folder path, I can create a new version of it in the randomly generated temp folder.
$tempdestination = New-Item -Path $newpath -ItemType director
Once this is in place, I can copy the files from each group to the corresponding temp folder.
$_.group | copy -Destination $tempdestination
The end result is the same directory structure for all of the XML files.
Once all of the files are copied, in the End scriptblock, I can create the archive getting the top folder in the temp directory, in this case Work. My solution assumes you will only have a single folder in the root of the temporary directory.
$top = dir $ziptemp –Directory Compress-Archive -Path $top.fullname -DestinationPath $zip
When the zip file is finished I delete the temporary folder and files.
An archive with folders (Image Credit: Jeff Hicks)
expand-archive -Path C:\work\XMLData.zip -DestinationPath E:\XMLData
Be aware that the target folder must already exist or you will get an error. Likewise, If the same file exists in the target PowerShell will throw an exception. You can solve that by using –Force.
expand-archive -Path C:\work\XMLData.zip -DestinationPath E:\XMLData -force
Expanding will also recreate any folders.
Viewing expanded folders (Image Credit: Jeff Hicks)
More in PowerShell
Most popular on petri