Advanced Event 1 – 2013 Powershell Scripting Games

The games have finally started!  If you have not registered to participate, you can still jump in, however, the first event submissions are closed and we are in the midst of voting.

This year, things are being run a little differently.  For one, the games are being run by Powershell.Org.  This is a community-owned and run site dedicated to promoting the education and adoption of Powershell.I thought I would take a few minutes and post my solution to the Advanced 1 Event and provide some thoughts about a couple of lessons I learned.  If you are interested, you can find the specs on Advanced Event 1 here.

Function Move-OldLogFile
{
<#
 .SYNOPSIS
 Archives log files after certain number of days.

.DESCRIPTION
 Given a folder of log files, the script will archive files that
 are older than the specified number of days.

.PARAMETER LogDirectory
 The source directory where the log files reside.

.PARAMETER NumberOfDays
 The maximum age of files to be left in the source directory.

 .PARAMETER ArchiveDirectory
 The directory where log files older than 'NumberOfDays' will
 be moved to.

.EXAMPLE
 PS C:\> Move-OldLogFile -LogDirectory c:\logfiles -NumberOfDays 90 -ArchiveDirectory c:\archivedirectory

 This example will check the files in the c:\logfiles directory for any files that
 are older than 90 days and move those files to c:\archivedirectory folder.

.INPUTS
 System.String,System.Int32

.OUTPUTS
 System.String

.NOTES
 Additional information about the function go here.

#>

[CmdletBinding()]
 Param(
 [Parameter(Mandatory=$true)]
 [ValidateScript({Test-Path $_})]
 [string]$LogDirectory,
 [Parameter(Mandatory=$true)]
 [int]$NumberOfDays,
 [Parameter(Mandatory=$true)]
 [ValidateScript({Test-Path $_})]
 [string]$ArchiveDirectory
 )

 Get-ChildItem -Path $logDirectory -Include *.log -Recurse |
 Where-Object{$_.LastwriteTime -lt (get-date).AddDays(-$numberOfDays)} |
 ForEach-Object {
 try
 {
 $DestinationName = Join-Path -Path $archiveDirectory -ChildPath $_.Directory.Name
 if (-not (Test-Path -Path $DestinationName))
 {
 New-Item -Path $DestinationName -ItemType Directory
 }
 Move-Item -Path $($_.FullName) -Destination $DestinationName
 }
 catch [Exception]
 {
 Write-Error -Message $_.Exception.Message
 }
 }
}

One of the first things I can tell you when you enter the Scripting Games, is this: TEST YOUR SCRIPTS THOROUGHLY! And test for error conditions to make sure that your script behaves as you expect when something is not quite right.  One of the first things that caught me off guard when writing this script is that I failed one of the requirements of this event.  The instructions specified that no output should be displayed unless there is an error.  In my script, I check to see if the destination folder exists (line 56) and create it if it is not there (line 58).  What I forgot in testing is that the New-Item cmdlet outputs information about the item created.  I should have piped that line to Out-Null so that the information gets redirected away from the console.  I probably could have picked up a little higher score had I done that.

I have looked back at my method for creating this script and I see where I missed this.  To begin, I set up a script that generated some test log files in a directory similar to what was specified in the event instructions.  While I did have the items in the “archives” folder deleted, I did not realize that the subfolders were not deleted.  This is important, because if the subfolders had not been there, I would have seen the output and corrected it.

Another issue that was pointed out to me was the names on my parameters (LogDirectory and ArchiveDirectory).  While these are not wrong, they are not in the proper style for Powershell.  Most other cmdlets use “Path” and “Destination” which would have been a better choice, and I will certainly try to keep that in mind on my next script.  Had I looked a little closer at style instead of having my head so much in the terms used in the event instructions, I probably would have caught this as well.  This does not prevent the script from working, but I’m sure I lost a few tenths of a point or more because of “style cops.” (And I use that term respectfully, here.)

Finally, I was reading a blog post by Don Jones about this event.  About two-thirds of the way down, he mentions using “$_.Exception.Message.”  On line 64 of my script, you will see that I used exactly that.  Don makes an excellent point that the $_ variable in Powershell can get hijacked easily and would be better to capture an error variable and use that directly.

I am sure there are other things that I could have done to better my script and I have gotten some really great feedback from the crowd voting that has been happening.  You are welcome to leave additional feedback in the comments below of how you think something could be better, or even if there is something you like about what I did.  As a learning experience, this is a great opportunity to sharpen our skills.

Look forward to seeing you all for the rest of the Scripting Games!

Advertisements
This entry was posted in Advanced Function, Cmdlet Help, Scripting Games 2013 and tagged , . Bookmark the permalink.

2 Responses to Advanced Event 1 – 2013 Powershell Scripting Games

  1. Luesec says:

    Thank you for Sharing what you learned from the comments. Sadly we can’t See just ours.

    A thing that i still have to look up is the difference between catch {} and catch [exception]{} and whats the pro and con.

    Good Point regarding Common Parameter naming. Will also try to improve my namings 🙂
    Regards

  2. Thanks for the comment. I had thought about seeing other comments posted on a submission when I am reviewing a script, but then thought it might pollute my own thinking and skew my scoring. So, while I too would like to see them, I understand why we can’t. Maybe we can see all comments on a particular submission when voting is closed.

    Glad you can take something away from what I learned. That is the point of my post and the games!

    Take care!

Comments are closed.