powershellziptarget7zipget-childitem

PowerShell script not zipping correct files


 Function Zip
{
    Param
    (
        [string]$zipFile
        ,
        [string[]]$toBeZipped
    )
    $CurDir = Get-Location
    Set-Location "C:\Program Files\7-Zip"
    .\7z.exe A -tzip $zipFile $toBeZipped | Out-Null
    Set-Location $CurDir
}
$Now = Get-Date
$Days = "60"
$TargetFolder = "C:\users\Admin\Downloads\*.*"
$LastWrite = $Now.AddDays(-$Days)
$Files = Get-Childitem $TargetFolder -Recurse | Where {$_.LastWriteTime -le "$LastWrite"}
$Files
Zip C:\Users\Admin\Desktop\TEST.zip $Files

I am testing out this script I found online. My problem is that instead of zipping the files in the target folder, it is copying and zipping the contents of the 7-zip program file folder. What could cause this? Thanks in advance


Solution

  • Pass the files as full paths to the Zip function, using their .FullName property (PSv3+ syntax):

    Zip C:\Users\Admin\Desktop\TEST.zip $Files.FullName
    

    The problem is that, in Windows PowerShell, the [System.IO.FileInfo] instances returned by Get-ChildItem situationally[1] stringify to their file names only, which is what happened in your case, so your Zip function then interpreted the $toBeZipped values as relative to the current location, which is C:\Program Files\7-Zip at that point.

    That said, it's better not to use Set-Location in your function altogether, so that in the event that you do want to pass actual relative paths, they are correctly interpreted as relative to the current location:

    Function Zip {
        Param
        (
            [Parameter(Mandatory)] # make sure a value is passed          
            [string]$zipFile
            ,
            [Parameter(Mandatory)] # make sure a value is passed
            [string[]]$toBeZipped
        )
        # Don't change the location, use & to invoke 7z by its full path.
        $null = & "C:\Program Files\7-Zip\7z.exe" A -tzip $zipFile $toBeZipped
        # You may want to add error handling here.
    }
    

    [1] When Get-ChildItem output stringifies to file names only:

    Note:

    The following therefore only applies to Get-ChildItem in Windows PowerShell:

    The problem is twofold:

    Always passing the .FullName property values instead is the simplest workaround or, for reliable operation with any PowerShell provider, not just the filesystem, .PSPath.

    [System.IO.FileInfo] and [System.IO.DirectoryInfo] instances output by a Get-ChildItem command stringify to their file names only, if and only if:

    Example commands:

    # NAME-ONLY stringification:
    
    Get-ChildItem | % ToString # no target path
    
    Get-ChildItem . | % ToString # path is literal dir.
    
    Get-ChildItem . *.txt | % ToString  # path is literal dir., combined with -Filter
    
    # FULL PATH stringification:
    
    Get-ChildItem foo* | % ToString # non-literal path (wildcard)
    
    Get-ChildItem -Recurse -Include *.txt | % ToString # use of -Include
    
    Get-ChildItem file.txt | % ToString # *file* path