Search code examples
powershellfindcompressionpattern-matchingparent

Powershell find and add files to zip by matching filename and its parent folder-name


Need powershell script for the below:

provided comma-separated filenames with parent folder like below:

$itemsToInclude = "stage\arsw.war,dev\deployfile"

I wish to search recursively for all comma-separated files mentioned in $itemsToInclude under the $workspace="D:\ims" location

and include only the files where BOTH the filename and the parent folder-name matches into hello.zip

Thus D:\ims\work1\app1\src\stage\arsw.war should be included in hello.zip while D:\ims\work1\app1\src\wow\arsw.war should not because the parent folder should be stage as mentioned in the $itemsToInclude

Thus expected result is that hello.zip should contain the following files.

D:\ims\work1\myapp2\src\dev\deployfile\file1.txt
D:\ims\work1\myapp2\src\dev\deployfile\file2.txt
D:\ims\work1\app1\src\stage\arsw.war

Tried the below powershell but it failed:

PS D:\ims> cd D:\ims

$workspace = “D:\ims” 
$itemsToInclude = “stage\arsw.war,dev\deployfile”

$items = $itemsToInclude -split “,” | ForEach-Object { $_.Trim() }

$matchingFiles = @()
foreach ($item in $items) { 
# Get the file name and the parent folder name from the item 

Write-Host "Split-Path -Path $item -Leaf"

$fileName = Split-Path -Path $item -Leaf

Write-Host "Split-Path -Path $item -Parent"

$parentFolder = Split-Path -Path $item -Parent

Write-Host "$fileName and $parentFolder"

$files = Get-ChildItem -Path $workspace -Filter $fileName -Recurse -File -ErrorAction SilentlyContinue | Where-Object { $_.Directory.Name -eq $parentFolder }


# Add the matching files to the array using array subexpression operator
$matchingFiles = @($matchingFiles; $files)

Write-Host "FOUND $($files.FullName) and $($matchingFiles.FullName)"

if ($matchingFiles) {
Compress-Archive -Path $matchingFiles -DestinationPath hello.zip 
} 
else { 

Write-Host “No matching files were found in $workspace” }

}

Output:

Split-Path -Path stage\arsw.war -Leaf
Split-Path -Path stage\arsw.war -Parent
arsw.war and stage
FOUND D:\ims\work1\app1\src\stage\arsw.war and D:\ims\work1\app1\src\stage\arsw.war
Compress-Archive : The path 'arsw.war' either does not exist or is not a valid file system path.
At line:33 char:1
+ Compress-Archive -Path $matchingFiles -DestinationPath hello.zip
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (arsw.war:String) [Compress-Archive], InvalidOperationException
    + FullyQualifiedErrorId : ArchiveCmdletPathNotFound,Compress-Archive
 
Split-Path -Path dev\deployfile -Leaf
Split-Path -Path dev\deployfile -Parent
deployfile and dev
FOUND  and D:\ims\work1\app1\src\stage\arsw.war
Compress-Archive : The path 'arsw.war' either does not exist or is not a valid file system path.
At line:33 char:1
+ Compress-Archive -Path $matchingFiles -DestinationPath hello.zip
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (arsw.war:String) [Compress-Archive], InvalidOperationException
    + FullyQualifiedErrorId : ArchiveCmdletPathNotFound,Compress-Archive

There are a couple of issues with this attempt.

  1. Files inside dev\deployfile is not Found although it is present.

  2. The files under dev\deployfile are not added to hello.zip

Kindly assist.


Solution

  • A simplified way to do it is by creating a regex pattern to match on the item's .FullName property. The following would only include those files in your Zip Archive, however, it's worth noting that the file's hierarchy is lost in the compressed file. They are placed in Zip's root.

    $itemsToInclude = 'stage\arsw.war,dev\deployfile'
    $pattern = $itemsToInclude.Split(',').ForEach({ [regex]::Escape($_) }) -join '|'
    $workspace = 'D:\ims'
    Get-ChildItem $workspace -Recurse -File |
        Where-Object FullName -Match $pattern |
        Compress-Archive -DestinationPath hello.zip
    

    Also, worth considering, if hello.zip already exists you would need to use the -Update switch.