From c177884ed30c37d90d2b80f7b5fcef2606a72a5c Mon Sep 17 00:00:00 2001 From: Santiago Squarzon Date: Tue, 20 Aug 2024 12:56:37 -0300 Subject: [PATCH 1/4] add -Exclude parameter to Compress-ZipArchive --- .../Commands/CompressZipArchiveCommand.cs | 57 +++++++++++++++++-- tests/CompressZipArchive.tests.ps1 | 10 ++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/PSCompression/Commands/CompressZipArchiveCommand.cs b/src/PSCompression/Commands/CompressZipArchiveCommand.cs index 84a6dab..344d7f1 100644 --- a/src/PSCompression/Commands/CompressZipArchiveCommand.cs +++ b/src/PSCompression/Commands/CompressZipArchiveCommand.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Linq; using System.Management.Automation; using PSCompression.Extensions; using static PSCompression.Exceptions.ExceptionHelpers; @@ -17,12 +18,14 @@ public sealed class CompressZipArchiveCommand : PSCmdlet, IDisposable private bool _isLiteral; - private string[] _paths = Array.Empty(); + private string[] _paths = []; private ZipArchive? _zip; private FileStream? _destination; + private WildcardPattern[]? _excludePatterns; + private readonly Queue _queue = new(); [Parameter( @@ -72,6 +75,11 @@ public string[] LiteralPath [Parameter] public SwitchParameter PassThru { get; set; } + [Parameter] + [SupportsWildcards] + [ValidateNotNullOrEmpty] + public string[]? Exclude { get; set; } + protected override void BeginProcessing() { if (!HasZipExtension(Destination)) @@ -100,6 +108,17 @@ protected override void BeginProcessing() { ThrowTerminatingError(StreamOpenError(Destination, e)); } + + const WildcardOptions wpoptions = WildcardOptions.Compiled + | WildcardOptions.CultureInvariant + | WildcardOptions.IgnoreCase; + + if (Exclude is not null) + { + _excludePatterns = Exclude + .Select(e => new WildcardPattern(e, wpoptions)) + .ToArray(); + } } protected override void ProcessRecord() @@ -109,6 +128,11 @@ protected override void ProcessRecord() _queue.Clear(); foreach (string path in _paths.NormalizePath(_isLiteral, this)) { + if (ShouldExclude(_excludePatterns, path)) + { + continue; + } + if (!path.IsArchive()) { Traverse(new DirectoryInfo(path), _zip); @@ -159,6 +183,11 @@ private void Traverse(DirectoryInfo dir, ZipArchive zip) foreach (FileSystemInfo item in enumerator) { + if (ShouldExclude(_excludePatterns, item.FullName)) + { + continue; + } + if (item is DirectoryInfo directory) { _queue.Enqueue(directory); @@ -167,7 +196,7 @@ private void Traverse(DirectoryInfo dir, ZipArchive zip) FileInfo file = (FileInfo)item; - if (ItemIsDestination(file, Destination)) + if (ItemIsDestination(file.FullName, Destination)) { continue; } @@ -265,8 +294,28 @@ private bool HasZipExtension(string path) => System.IO.Path.GetExtension(path) .Equals(".zip", StringComparison.InvariantCultureIgnoreCase); - private bool ItemIsDestination(FileInfo source, string destination) => - source.FullName.Equals(destination, StringComparison.InvariantCultureIgnoreCase); + private bool ItemIsDestination(string source, string destination) => + source.Equals(destination, StringComparison.InvariantCultureIgnoreCase); + + private static bool ShouldExclude( + WildcardPattern[]? patterns, + string path) + { + if (patterns is null) + { + return false; + } + + foreach (WildcardPattern pattern in patterns) + { + if (pattern.IsMatch(path)) + { + return true; + } + } + + return false; + } protected override void EndProcessing() { diff --git a/tests/CompressZipArchive.tests.ps1 b/tests/CompressZipArchive.tests.ps1 index 3dfeb9c..67ad7e3 100644 --- a/tests/CompressZipArchive.tests.ps1 +++ b/tests/CompressZipArchive.tests.ps1 @@ -104,4 +104,14 @@ Describe 'Compress-ZipArchive' -Tag 'Compress-ZipArchive' { Get-ChildItem skipitself -Recurse | ForEach-Object Name | Should -Not -Contain $zipname } + + It 'Should skip items that match the exclusion patterns' { + Remove-Item "$extractpath.zip" -Force + Compress-ZipArchive $testpath $extractpath -Exclude *testfile00*, *testfolder05* + Expand-Archive "$extractpath.zip" $extractpath + Get-ChildItem $extractpath -Recurse | ForEach-Object { + $_.FullName | Should -Not -BeLike *testfile00* + $_.FullName | Should -Not -BeLike *testfolder05* + } + } } From 8b6942875933f485b295f73ab4800952cf3e1fea Mon Sep 17 00:00:00 2001 From: Santiago Squarzon Date: Tue, 20 Aug 2024 13:37:32 -0300 Subject: [PATCH 2/4] add parameter to doc --- docs/en-US/Compress-ZipArchive.md | 42 ++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/docs/en-US/Compress-ZipArchive.md b/docs/en-US/Compress-ZipArchive.md index 2bd4e7d..1177d3d 100644 --- a/docs/en-US/Compress-ZipArchive.md +++ b/docs/en-US/Compress-ZipArchive.md @@ -106,6 +106,20 @@ Get-ChildItem .\path -Recurse -Directory | Compress-ZipArchive -Destination dest.zip -Update ``` +### Example 8: Exclude files and folders from source + +```powershell +Compress-ZipArchive .\path -Destination myPath.zip -Exclude *.xyz, *\test\* +``` + +This example shows how to compress all items in `path` excluding all files having a `.xyz` extension and excluding +a folder with name `test` and all folder child items. + +> [!TIP] +> +> The `-Exclude` parameter supports [wildcard patterns](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_wildcards?view=powershell-7.4&viewFallbackFrom=powershell-7.3), +exclusion patterns are tested against the items `.FullName` property. + ## PARAMETERS ### -Path @@ -117,9 +131,9 @@ This parameter accepts wildcard characters. Wildcard characters allow you to add > [!TIP] > Using wildcards with a root directory affects the archive's contents: > -> - To create an archive that includes the root directory, and all its files and subdirectories, specify the root directory in the Path without wildcards. For example: `-Path C:\Reference` -> - To create an archive that excludes the root directory, but zips all its files and subdirectories, use the asterisk (`*`) wildcard. For example: `-Path C:\Reference\*` -> - To create an archive that only zips the files in the root directory, use the star-dot-star (`*.*`) wildcard. Subdirectories of the root aren't included in the archive. For example: `-Path C:\Reference\*.*` +> - To create an archive that includes the root directory, and all its files and subdirectories, specify the root directory in the Path without wildcards. For example: `-Path C:\Reference` +> - To create an archive that excludes the root directory, but zips all its files and subdirectories, use the asterisk (`*`) wildcard. For example: `-Path C:\Reference\*` +> - To create an archive that only zips the files in the root directory, use the star-dot-star (`*.*`) wildcard. Subdirectories of the root aren't included in the archive. For example: `-Path C:\Reference\*.*` ```yaml Type: String[] @@ -187,6 +201,28 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Exclude + +Specifies an array of one or more string patterns to be matched as the cmdlet gets child items. +Any matching item is excluded from the created zip archive. +Wildcard characters are accepted. + +> [!NOTE] +> +> - Patterns are tested against the object's `.FullName` property. + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: True +``` + ### -Update Updates the specified archive by replacing older file versions in the archive with newer file versions that have the same names. You can also use this parameter to add files to an existing archive. From 8bde7f05564443ed1ddfb25dd1c168e5b2be8ce9 Mon Sep 17 00:00:00 2001 From: Santiago Squarzon Date: Tue, 20 Aug 2024 13:42:41 -0300 Subject: [PATCH 3/4] add parameter to doc --- docs/en-US/Compress-ZipArchive.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/Compress-ZipArchive.md b/docs/en-US/Compress-ZipArchive.md index 1177d3d..7ee2e42 100644 --- a/docs/en-US/Compress-ZipArchive.md +++ b/docs/en-US/Compress-ZipArchive.md @@ -113,7 +113,7 @@ Compress-ZipArchive .\path -Destination myPath.zip -Exclude *.xyz, *\test\* ``` This example shows how to compress all items in `path` excluding all files having a `.xyz` extension and excluding -a folder with name `test` and all folder child items. +a folder with name `test` and all its child items. > [!TIP] > From 5e582af2293a504c917889bea9bf0df554c463bf Mon Sep 17 00:00:00 2001 From: Santiago Squarzon Date: Tue, 20 Aug 2024 13:43:36 -0300 Subject: [PATCH 4/4] add parameter to doc --- docs/en-US/Compress-ZipArchive.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/en-US/Compress-ZipArchive.md b/docs/en-US/Compress-ZipArchive.md index 7ee2e42..9dbf306 100644 --- a/docs/en-US/Compress-ZipArchive.md +++ b/docs/en-US/Compress-ZipArchive.md @@ -208,8 +208,7 @@ Any matching item is excluded from the created zip archive. Wildcard characters are accepted. > [!NOTE] -> -> - Patterns are tested against the object's `.FullName` property. +> Patterns are tested against the object's `.FullName` property. ```yaml Type: String[]