Search code examples
powershellcompressionziphidden-files

How to zip / archive hidden files using Compress-Archive?


When zipping folders using Compress-Archive it skips all hidden files.

The Documentation page tells us that this cmdlet uses Microsoft .NET Framework API System.IO.Compression.ZipArchive in the background.

Is there some way to force it to archive hidden files? I cannot find this issue documented anywhere. I tried -Force for the heck of it, didn't help.

My current workaround is to use Set-FileAttribute to remove the hidden attribute before zipping.


Solution

  • This looks like a bug/oversight in the Compress-Archive cmdlet. Since the cmdlet provides no "include hidden files" parameter but does accept a collection of source files via the -Path or -LiteralPath parameters, I would expect either this...

    Compress-Archive -Path (
        Get-ChildItem -Path '...' -Force `
            | Select-Object -ExpandProperty 'FullName' `
    ) -DestinationPath '...'
    

    ...or this...

    Get-ChildItem -Path '...' -Force | Compress-Archive -DestinationPath '...'
    

    ...to work as a way of passing hidden files to the cmdlet; the key being specifying the -Force parameter for Get-ChildItem. Both of those invocations, however, throw these errors...

    Get-Item : Could not find item ....
    At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Archive\Microsoft.PowerShell.Archive.psm1:814 char:63
    + ... Entry.LastWriteTime = (Get-Item -LiteralPath $currentFilePath).LastWr ...
    +                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (...:String) [Get-Item], IOException
        + FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetItemCommand
    
    Exception setting "LastWriteTime": "Cannot convert null to type "System.DateTimeOffset"."
    At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Archive\Microsoft.PowerShell.Archive.psm1:814 char:25
    + ...             $currentArchiveEntry.LastWriteTime = (Get-Item -LiteralPa ...
    +                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException
        + FullyQualifiedErrorId : ExceptionWhenSetting
    

    ...for the first hidden file in the input list. (Note that invoking the first snippet without Select-Object -ExpandProperty 'FullName' instead throws Compress-Archive : The path '...' either does not exist or is not a valid file system path..)

    On my system, the referenced lines 812-814 of Microsoft.PowerShell.Archive.psm1 are...

    # Updating  the File Creation time so that the same timestamp would be retained after expanding the compressed file. 
    # At this point we are sure that Get-ChildItem would succeed.
    $currentArchiveEntry.LastWriteTime = (Get-Item -LiteralPath $currentFilePath).LastWriteTime
    

    So, even if we pass -Force to Get-ChildItem to get the paths of hidden file objects to pass to Compress-Archive, internally the cmdlet is fetching those file objects again using Get-Item...but it's not passing -Force, which of course will fail (despite what the comment on the previous line claims). Thus, I don't think there's any way to get Compress-Archive to work with hidden files without either you or Microsoft editing that script.