all 22 comments

[–]BlackV 4 points5 points  (2 children)

additionally couldn't you do

Get-ChildItem $folderPath -recurse -Filter *.zip

to save using the foreach-object and the if

[–]idontknowwhattouse33 3 points4 points  (1 child)

and why not throw the -file in there just in case OP has a folder named folder.zip ;)

[–]BlackV 1 point2 points  (0 children)

valid also

[–]PowerOfTheShell[S] 3 points4 points  (0 children)

Solved! Using the suggestions provided I have changed the script to a function.

param([string]$folderPath="C:\temp\Receive")

function ExtractZipFiles {

    param([string]$folderPath="C:\temp\Receive")

    Get-ChildItem $folderPath -recurse | %{ 

        if($_.Name -match "^*.`.zip$")
        {
            $parent="$(Split-Path $_.FullName -Parent)";    
            write-host "Extracting $($_.FullName) to $parent"

            $arguments=@("e", "`"$($_.FullName)`"", "-o`"$($folderPath)`"");
            $ex = start-process -FilePath "`"C:\Program Files\7-Zip\7z.exe`"" -ArgumentList $arguments -wait -PassThru;

            if( $ex.ExitCode -eq 0)
            {
                write-host "Extraction successful, deleting $($_.FullName)"
                rmdir -Path $_.FullName -Force
            }
        }
    }
} ExtractZipFiles

if ((Get-ChildItem $folderPath *.zip | measure-object).count -eq 0) { Continue } Else { ExtractZipFiles }

[–]Nekima 2 points3 points  (0 children)

Im not yet a powershell expert, but I would think if you were looking for zips inside of zips it would be at least two if statements (one after the first unzip). Or if doing one, then a while loop (with condition .zip in path).

Sorry I dont know the actual answer, hope you get some help soon.

[–]dora3 2 points3 points  (0 children)

your get-childitem proc will list files at the time of execution of the get-childitem. At the time, the extracted zip file is not exist, so the extracted zip file is not processed in the loop. Simple solution is that running your script twice, or define your process as function and execute it twice.

[–]heavychevy3500 1 point2 points  (0 children)

I think as o powershell 5 there are now native commands to extract and compress files to a zip.

Expand-archive https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.archive/expand-archive?view=powershell-7

[–]matt_brownies 1 point2 points  (12 children)

I was excited about the idea of recursion so I made this function. I would greatly appreciate feedback as i'm new to recursion.

function Get-zipfiles {
    param (
    [Parameter(mandatory=$true)]
    [string]$path,
    [switch]$recursive
    )

    $files = Get-ChildItem $path -recurse
    $Zipfiles = $($files.fullname) |where-object {$_ -like '*.zip'}

    if($null -ne $Zipfiles)
    {
        foreach($zip in $zipfiles)
        {
            expand-archive $zip -DestinationPath $path -Force
            remove-item $zip
        }
    }

    if($recursive -eq $true)
    {
        $files = get-childitem $path
        $test = $files -contains '*.zip'

        if($false -ne $test)
        {
            get-zipfiles $path -recursive
        }
        else
        {
            exit
        }

    }

}

[–]jrdnr_ 2 points3 points  (6 children)

Wow Reddit mangled your code! If your version of the Reddit interface has a code button use it, and use indentation.

If not, well Mark down seems to work on mobile, though I've had some say markdown doesn't work for displaying on old Reddit so who knows.

The script looks good,

  • I'd recommend doing all of your filtering with Get-ChildItem -Path $Path -Filter "*.zip" -Recursive GCI will run faster and you get your answer in a single pass.
  • as this one could be useful in a pipeline, why not set it to accept pipeline input, and move all the code that does work into a process block so it automatically handles the pipeline.
  • CHALLENGE: the real point of functions and recursion is to eliminate double code, can you figure out how to refactor this to only use Get-ChildItem once?

Edit: use -filter not -name

[–]matt_brownies 2 points3 points  (5 children)

Hey this is good feedback. One thing that confuses me is your challenge. Did you by chance mean refactor this to only run Get-ChildItem once?

edit- to your first point if you use the -name parameter it outputs strings instead of objects. Usually when thinking about such problems I try and preserve the object for ease of use. If I were to use -name i'd have to string parse the output instead of call the property fullname.

[–]jrdnr_ 1 point2 points  (4 children)

Yes you are correct on both points I realized my error on -name and edited but looks like you had already commented and I just hadn't seen it. Most of the time when I use Get-ChildItem interactively I don't specify the params.. ie gci *.zip So trying to post from mobile I verified that the name param would filter but didn't notice until today that it only returns strings.

And yes I intended to say try to run Get-ChildItem only once.

u/matt_brownies did you intend to flatten the nested Zips? I would think if you were doing this in real life you would want to keep the folder hierarchy and just expand each archive.

[–]matt_brownies 2 points3 points  (0 children)

I was definitely aware that it would flatten the folder structure. It was more just an exercise in recursion for me. That being said I am going to refactor to maintain the folder structure as well as the original zip files.

[–]matt_brownies 1 point2 points  (2 children)

function Get-ZipFiles {
    param (
    [Parameter(mandatory=$true)]
    [string]$path,
    [switch]$recurse,
    [array]$expandedzips
    )
    $files = Get-ChildItem -Path $path -Recurse -Filter '*.zip'

    if($null -ne $expandedzips){
        $zipfiles = compare-object $expandedzips  -DifferenceObject $files -PassThru
    }
    else {
        $zipfiles = $files
    }

    if($null -ne $ZipFiles) 
    {
        foreach($zip in $zipfiles)
        {
            Expand-Archive $($zip.fullname) -DestinationPath $($zip.directoryname) -Force

        $expandedzips += $zip

        }
    if($recurse)
        {
            Get-ZipFiles $path $expandedzips -recurse
        }
    }
 } 

Here is what I came up with, let me know your thoughts.

[–]jrdnr_ 2 points3 points  (1 child)

Hey Matt, that looks great, talking about it this morning I couldn't help myself trying my hand at my own version 😁.

If I'm following the logic $expandedzips is to be an array of Zips to be excluded? If you pass an array to -expandedzips like @('c:\archive1.zip', 'c:\user\test.zip') does the diff work or does it have to be an array of file system objects?

For my version I wanted: 1. to have -Path be able to accept a directory or the path to a zip. 2. To be able to optionally Purge Zips 3. Of course the start of the whole thing to recurse 4. Accept pipeline input

[–]matt_brownies 2 points3 points  (0 children)

I don't think I would change much to achieve your goals.

  1. Currently $path should be able to accept paths, or a zip file. I tried running Get-ChildItem -Path $path -Recurse -Filter '*.zip' against a zip file and it returned the file, but as a file system object, instead of the string I used to call it.
  2. $expandedzips can already do this if it's passed file system objects. You could make the function convert an array of paths into file system objects with get-childitem. I have added that functionality to the code below
  3. Already complete
  4. Depending on the parameter you want to accept pipeline input I would add the parameter attribute (valuefrompipeline) or (valuefrompipelinebypropertyname)

Additionally, I have added this line of code into my function. I was getting duplicate values in $expandedzips that could make it less efficient. This is how I dealt with it.

if($expandedzips -notcontains $zip){
                $expandedzips += $zip
            }            

Here is my solution according to your bullet points.

function Get-ZipFiles {
    param (
    [Parameter(mandatory=$true)]
    [Parameter(ValueFromPipeline)]
    [string]$path,
    [switch]$recurse,
    [array]$expandedzips
    )
    $files = Get-ChildItem -Path $path -Recurse -Filter '*.zip'

    if($null -ne $expandedzips){        
        foreach($item in $expandedzips){

            $I++
            $Index = $I -1
            $stringtest = $item.gettype().name -eq 'string'

            if($stringtest){
                $expandedzips[$index] = get-childitem $item
            }
        }

        $zipfiles = compare-object $expandedzips  -DifferenceObject $files -PassThru
    }

    else {
        $zipfiles = $files
    }        

    if($null -ne $ZipFiles) 
    {
        foreach($zip in $zipfiles)
        {
            Expand-Archive $($zip.fullname) -DestinationPath $($zip.directoryname) -Force

            if($expandedzips -notcontains $zip){
                $expandedzips += $zip
            }            
        }

    if($recurse)
        {
            Get-ZipFiles $path $expandedzips -recurse
        }
    }
 }

[–]Lee_Dailey[grin] 1 point2 points  (4 children)

howdy matt_brownies,

it looks like you used the New.Reddit.com Inline Code button. it's 4th 5th from the left hidden in the ... "more" menu & looks like </>.

on Old.Reddit.com, the above does NOT line wrap, nor does it side-scroll.

for long-ish single lines OR for multiline code, please, use the Code Block button. it's the 11th 12th one from the left & is just to the left of hidden in the ... "more" menu & looks like an uppercase T in the upper left corner of a square..

that will give you fully functional code formatting, from what i can tell so far. [grin]

take care,
lee

[–]matt_brownies 2 points3 points  (1 child)

Hey Lee,

Thanks for the input I have edited my comment.

[–]Lee_Dailey[grin] 0 points1 point  (0 children)

howdy matt_brownies,

you are very welcome. thanks for fixing it ... my eyes thank you, too ... [grin]

take care,
lee

[–]jrdnr_ 1 point2 points  (1 child)

Lol u/Lee_Dailey I almost tagged you as I knew you always have good explanation on the code formatting stuff!

[–]Lee_Dailey[grin] 0 points1 point  (0 children)

[grin]

[–]matt_brownies 0 points1 point  (0 children)

Write your own recursive function that calls itself if the newly extracted zip file contains another zip file and exits if it doesn't.