Home > Back-end >  How to optimize my loops if I already have loops that iterate over once
How to optimize my loops if I already have loops that iterate over once

Time:03-22

I am new to powershell. I have created a script to copy files according to year & month, and then zip it. My code is working fine, because I am noob in powershell, and just wrote same command again and again. Can someone help me reduce my lines in the codes?

$Source = "C:\Users\abhishek.y.verma\OneDrive - Accenture\Desktop\My-Info"
$Target = "C:\Users\abhishek.y.verma\OneDrive - Accenture\Desktop\Power"

$Year1 = 2022
$Year2 = 2021
$Year3 = 2020
$Year4 = 2019
$Year5 = 2018


$files = Get-ChildItem -LiteralPath $Source -Recurse -File 

#Get the names of the files
$filesFromYear1 = $files | Where-Object {$_.CreationTime.Year -eq $Year1}
$filesFromYear2 = $files | Where-Object {$_.CreationTime.Year -eq $Year2}
$filesFromYear3 = $files | Where-Object {$_.CreationTime.Year -eq $Year3}
$filesFromYear4 = $files | Where-Object {$_.CreationTime.Year -eq $Year4}





#Make Folder for each month
foreach($i in 1..12) {
    New-Item -Path $Target\$Year1 -Name $i -ItemType "directory"
    New-Item -Path $Target\$Year2 -Name $i -ItemType "directory"
    New-Item -Path $Target\$Year3 -Name $i -ItemType "directory"
    New-Item -Path $Target\$Year4 -Name $i -ItemType "directory"
    New-Item -Path $Target\$Year5 -Name $i -ItemType "directory"

}

#Move Files:

foreach($j in 1..12) {
    $filesMonthly1 = $files | Where-Object  { ($_.CreationTime.Year -eq $Year1) -and ($_.CreationTime.Month -eq $j) }
    $filesMonthly1 | Copy-Item -Destination $Target\$Year1\$j
    Compress-Archive -Path $Target\$Year1\$j -Destination $Target\$Year1\$j.zip
    $filesMonthly2 = $files | Where-Object  { ($_.CreationTime.Year -eq $Year2) -and ($_.CreationTime.Month -eq $j) }
    $filesMonthly2 | Copy-Item -Destination $Target\$Year2\$j
    Compress-Archive -Path $Target\$Year2\$j -Destination $Target\$Year2\$j.zip
    $filesMonthly3 = $files | Where-Object  { ($_.CreationTime.Year -eq $Year3) -and ($_.CreationTime.Month -eq $j) }
    $filesMonthly3 | Copy-Item -Destination $Target\$Year3\$j
    Compress-Archive -Path $Target\$Year3\$j -Destination $Target\$Year3\$j.zip
    $filesMonthly4 = $files | Where-Object  { ($_.CreationTime.Year -eq $Year4) -and ($_.CreationTime.Month -eq $j) }
    $filesMonthly4 | Copy-Item -Destination $Target\$Year4\$j
    Compress-Archive -Path $Target\$Year4\$j -Destination $Target\$Year4\$j.zip
    $filesMonthly5 = $files | Where-Object  { ($_.CreationTime.Year -eq $Year5) -and ($_.CreationTime.Month -eq $j) }
    $filesMonthly5 | Copy-Item -Destination $Target\$Year5\$j
    Compress-Archive -Path $Target\$Year5\$j -Destination $Target\$Year5\$j.zip
}

CodePudding user response:

Use Where-Object once, to test that the year is one of the years you're interested in, then use Group-Object to group them all together - at which point you just need to repeat the same operation for each group:

$Source = "C:\Users\abhishek.y.verma\OneDrive - Accenture\Desktop\My-Info"
$Target = "C:\Users\abhishek.y.verma\OneDrive - Accenture\Desktop\Power"

$Years = 2022..2019

# discover files in scope
$files = Get-ChildItem -LiteralPath $Source -Recurse -File |Where-Object {$_.CreationTime.Year -in $Years}

# group by creation year
$files |Group-Object { $_.CreationTime.Year } |ForEach-Object {
  # extract year of current group
  $year = $_.Name

  foreach($month in 1..12){
    # create the target directory
    $newMonthFolder = New-Item -Path $Target\$year -Name $month -ItemType "directory"

    # copy relevant files
    $_.Group |Where-Object {$_.CreationTime.Month -eq $month} |Copy-Item -Destination $newMonthFolder.FullName

    # create zip
    Compress-Archive -LiteralPath $newMonthFolder.FullName -Destination $Target\$Year\$month.zip
  }
}

CodePudding user response:

Code

$Source = "C:/Users/Megam/Desktop/A-1"
$Target = "C:/Users/Megam/Desktop/B-1"
$initialYear = 2018
$finalYear = 2022

$validFiles = Get-ChildItem -LiteralPath $Source -Recurse -File | Where-Object { $_.CreationTime.Year -in ($initialYear..$finalYear)}
($initialYear..$finalYear) | ForEach-Object {
    $year = $_
    $files = $validFiles | Where-Object { $_.CreationTime.Year -eq $year} # Get files of year.
    if($files.Count -gt 0) # If there are files
    {
        New-Item -Path "$Target\$year" -ItemType Directory -Force | Out-Null # Create Directory - Year 
    }
    (1..12) | ForEach-Object{
        $month = $_
        $monthFiles = $files | Where-Object{ $_.CreationTime.Month -eq $month }
        if($monthFiles.Count -gt 0) # If there are files
        {
            $dest = "$Target/$year/$month"
            New-Item -Path $dest -ItemType Directory -Force | Out-Null # Create Directory - Month 
            $monthFiles | ForEach-Object{
                Copy-Item -Path $_ -Destination $dest -Force
                Write-Host "File: $_ , Year: $year, Month: $month, Destination: $dest/$($_ | Split-Path -Leaf)" #Remove this line in production
            }
            Compress-Archive -Path "$dest/*.*" -Destination "$dest/files-$year-$month.zip" -Force # Create Zip file
        }
    }
}

$_ it refers to object in current context, between(inside) curly braces. for example Where-Object { $_.LastWriteTime.Year -eq $year} in this case $_ is a file object, not int value from ForEach-Object{ ..... } context, previously I store the year value in $year variable for use in Where-Object{ ..... }context.
Same case for $month variable.

REMEMBER to remove this line in the code Write-Host "File: $_ , Year: $year, Month: $month, Destination: $dest/$($_ | Split-Path -Leaf)" #Remove this line in production, this line can slow down the execution in case of having many files

Example Output
Initial files
files
execution
output result files
enter image description here

  • Related