This is a continuation from my previous blog titled “2013 PowerShell Scripting Games Advanced Event 1 – Parameters Don’t Always Work As Expected“.
This isn’t the exact script, but sections of it. You’ll notice at the bottom of the first image shown below, I retrieve the list of folder names from the files variable to keep from having to make another call to the file system. Going from one variable to another in memory is a cheap operation where as going to disk to retrieve something is more expensive from a resources standpoint. I received this comment from a reviewer: “I thought this was pretty clever: $folders = $files.directory.name | Select-Object -Unique”.
Then I iterate through the list of folders one at a time instead of iterating through each file individually. I build my paths, storing them in variables. I check to see if the folder exists and create it if not, and then move the files a folder at a time. My goal was to make as few calls to the file system as possible.
It’s not only about writing a command that’s syntax is correct, or a command that works, but also a command that’s as efficient as possible. I’ve trimmed out the error checking and verbose output from these examples to make them easier to understand.
Want to see my actual solution? Click this link: Advanced 1: An Archival Atrocity: http://scriptinggames.org/entrylist.php?entryid=279. You’ll have to be signed in to see the solution. If you like my script, please vote for it!
Update 02/09/14
The link to my solution is no longer valid so I’m posting it here since I’ve received some requests for it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | function Move-LogFile { <# .SYNOPSIS Move files with a .log extension from the SourcePath to the DestinationPath that are older than Days. .DESCRIPTION Move-LogFile is a function that moves files with a .log extension from the first level subfolders that are specified via the SourcePath parameter to the same subfolder name in the DestinationPath parameter that are older than the number of days specified in the Days parameter. If a subfolder does not exist in the destination with the same name as the source, it will be created. This function requires PowerShell version 3. .PARAMETER SourcePath The parent path of the subfolders where the log files reside. Log files in the actual SourcePath folder will not be archived, only first level subfolders of the specified SourcePath location. .PARAMETER DestinationPath Parent Path of the Destination folder to archive the log files. The name of the original subfolder where the log files reside will be created if it doesn't already exist in the Destination folder. Destination subfolders are only created if one or more files need to be archived based on the days parameter. Empty subfolders are not created until needed. .PARAMETER Days Log files not written to in more than the number of days specified in this parameter are moved to the destination folder location. .PARAMETER Force Switch parameter that when specified overwrites destination files if they already exist. .EXAMPLE Move-LogFile -SourcePath 'C:\Application\Log' -DestinationPath '\\NASServer\Archives' -Days 90 .EXAMPLE Move-LogFile -SourcePath 'C:\Application\Log' -DestinationPath '\\NASServer\Archives' -Days 90 -Force #> [CmdletBinding()] param ( [string]$SourcePath = 'C:\Application\Log', [string]$DestinationPath = '\\NASServer\Archives', [int]$Days = 90, [switch]$Force ) BEGIN { Write-Verbose "Retrieving a list of files to be archived that are older than $($Days) days" try { $files = Get-ChildItem -Path (Join-Path -Path $SourcePath -ChildPath '*\*.log') -ErrorAction Stop | Where-Object LastWriteTime -lt (Get-Date).AddDays(-$days) } catch { Write-Warning $_.Exception.Message } $folders = $files.directory.name | Select-Object -Unique Write-Verbose "A total of $($files.Count) files have been found in $($folders.Count) folders that require archival" } PROCESS { foreach ($folder in $folders) { $problem = $false $ArchiveDestination = Join-Path -Path $DestinationPath -ChildPath $folder $ArchiveSource = Join-Path -Path $SourcePath -ChildPath $folder $ArchiveFiles = $files | Where-Object directoryname -eq $ArchiveSource if (-not (Test-Path $ArchiveDestination)) { Write-Verbose "Creating a directory named $($folder) in $($DestinationPath)" try { New-Item -ItemType directory -Path $ArchiveDestination -ErrorAction Stop | Out-Null } catch { $problem = $true Write-Warning $_.Exception.Message } } if (-not $problem) { Write-Verbose "Archiving $($ArchiveFiles.Count) files from $($ArchiveSource) to $($ArchiveDestination)" try { If ($Force) { $ArchiveFiles | Move-Item -Destination $ArchiveDestination -Force -ErrorAction Stop } Else { $ArchiveFiles | Move-Item -Destination $ArchiveDestination -ErrorAction Stop } } catch { Write-Warning $_.Exception.Message } } } } END { Remove-Variable -Name SourcePath, DestinationPath, Days, Force, files, folders, folder, problem, ArchiveDestination, ArchiveSource, ArchiveFiles -ErrorAction SilentlyContinue } } |
This PowerShell script can also be downloaded from the TechNet script repository.
µ
Hello Sir,
It has been a great learning going through your script. I think what you did here :
$folders = $files.directory.name | Select-Object -Unique
This is just awesome and efficient 🙂
Looking forward to learn and imbibe from your scripts and blog posts.
~Regards~
Dex