Behind the PowerShell Pipeline logo

Behind the PowerShell Pipeline

Subscribe
Archives
October 25, 2024

New Tools - File and Folder Archiver

Let's continue with recent additions to my PowerShell toolbox. Another recurring task is that I often want to create a ZIP file of selected files from a folder. For this newsletter, I occasionally create a zip file of content and share it on Dropbox. Using Compress-Archive is simple enough. The hurdle was selecting the files I wanted to include.

An Object Picker

If I only need to select files from a given folder, I can easily use Out-ConsoleGridView, since I'm using PowerShell 7 primarily. I could just as easily use Out-GridView. If you don't have Out-ConsoleGridView, install the Microsoft.PowerShell.ConsoleGuiTools module from the PowerShell Gallery.

Using the Out-ConsoleGridView cmdlet, I can select files from a folder

dir c:\temp\ -file | Out-ConsoleGridView -Title "Select files" -OutputMode Multiple

This works just like Out-GridView but in a console window. I can select multiple files and click OK.

Picking files with Out-ConsoleGridView
figure 1

The selected files are returned as objects.

Selected files from Out-ConsoleGridView
figure 2

New-FolderArchive

Because Compress-Archive accepts pipeline input, I can easily create a zip file with a one-line expression.

dir c:\temp\ -file | Out-ConsoleGridView -Title "Select files" -OutputMode Multiple | Compress-archive -DestinationPath c:\Work\selected.zip -CompressionLevel Optimal

To make this re-usable, I wrapped the code in a function called New-FolderArchive.

#requires -version 7.4
#requires -module Microsoft.PowerShell.ConsoleGuiTools

#this function could be revised to use Out-GridView instead of Out-ConsoleGridView.
Function New-FolderArchive {
    [cmdletbinding(SupportsShouldProcess)]
    [OutputType('System.IO.FileInfo')]
    Param(
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            HelpMessage = "Specify the folder to use."
        )]
        [ValidateNotNullOrEmpty()]
        [String]$Path = ".",
        [Parameter(HelpMessage ="Specify the file name and path for the ZIP archive.")]
        [ValidatePattern(".*\.zip$")]
        [string]$Archive = ".\Archive.zip"
    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN  ] Starting $($MyInvocation.MyCommand)"
        Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN  ] Running under PowerShell version $($PSVersionTable.PSVersion)"
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $Path"
        $selected = Get-ChildItem -Path $Path -file |
        Out-ConsoleGridView -Title "Select files to archive" -OutputMode Multiple
        if ($Selected) {
            Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Selected $($Selected.Count) files"
            #An existing archive with the same name will be overwritten
            if ($PSCmdlet.ShouldProcess("$($Selected.Count) selected files", "Archive to $Archive")) {
                $Selected | Compress-Archive -DestinationPath $Archive -CompressionLevel Optimal -Force -PassThru
            }
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeOfDay) END    ] Ending $($MyInvocation.MyCommand)"
    } #end

} #close New-FolderArchive

Even though Compress-Archive supports -WhatIf, I'm writing my own WhatIf handler so that I can control the message displayed when the function is run with -WhatIf.

if ($PSCmdlet.ShouldProcess("$($Selected.Count) selected files", "Archive to $Archive")) {
    $Selected | Compress-Archive -DestinationPath $Archive -CompressionLevel Optimal -Force -PassThru
}

You should use SupportsShouldProcess in the cmdletbinding attribute to enable the -WhatIf and -Confirm parameters whenever creating or modifying files.

PS C:\> New-FolderArchive -Path c:\work -Archive c:\temp\Demo.zip -WhatIf
What if: Performing the operation "Archive to c:\temp\Demo.zip" on target "12 selected files".

I expect this will meet most of my needs. However, there may be more complicated scenarios where I need to select files from multiple folders or filter files based on some criteria.

Zip Options

For more granular control over ZIP archives, I can use the System.IO.Compression class from the .NET Framework. I wrote about this earlier this year.

I can easily create a new ZIP file.

$zip = [System.IO.Compression.ZipFile]::Open("c:\work\FooFoo.zip","Create")

The file you create must not already exist or you'll get an error.

I like that I can add a comment to the ZIP file.

$zip.comment = "This is a demo zip file"
`````

This is already an advantage over `Compress-Archive`.

I have several options for adding items to the ZIP file. I can add a file.

```powershell
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip,"C:\Scripts\1000FemaleNames.txt","1000FemaleNames.txt","SmallestSize")

The parameter syntax is (ZipArchive, source file, entry name, compression level). The compression level can be NoCompression, Fastest, or Optimal. PowerShell 7 adds 'SmallestSize. The entry name is how the file will be stored in the ZIP file.

Creating a zip file entry
figure 3
Want to read the full issue?
GitHub Bluesky LinkedIn About Jeff
Powered by Buttondown, the easiest way to start and grow your newsletter.