Tag Archives: .NET

The Unzip Time Difference

As part of an upcoming deployment, I’ve been getting intimate with AWS OpsWorks and Chef. What I mean by Chef is, using the PowerShell resource in Chef. I was recently looking for a way to save some time on a deployment, when I considered removing the unzipping of files downloaded from S3. I wanted to know if there would be a time savings in getting the files to the EC2 Instance in their decompressed format.

This brought me over to my console to compare the newer Expand-Archive cmdlet and .NET. I’ve always considered that dropping down to .NET is a time savings.

In my Chef recipe I’m using .NET for decompression, as I’m deploying to Windows Server 2012 R2 and it includes PowerShell 4.0 by default. The Compress-Archive and Expand-Archive cmdlets were introduced in PowerShell 5.0. This isn’t to say I couldn’t get PowerShell 5.0 in place, but I needed to know if it would even be necessary.

I had a little testing to do. The below command measured the time it took to expand a 133MB zip file using .NET.

Measure-Command -Expression {
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    [System.IO.Compression.ZipFile]::ExtractToDirectory('C:\Users\tommymaynard\Desktop\HCM-920-UPD-018-WIN_1of10.zip','C:\Users\tommymaynard\Desktop\unzip\')
}

When the command was run five separate times, it resulted in following times: 9 seconds 393 milliseconds, 9 seconds 117 milliseconds, 9 seconds 455 milliseconds, 8 seconds 489 milliseconds, and 10 seconds 338 milliseconds. I wasn’t loosing any time by unzipping this file.

The below command does the same thing as the one above; however, it uses the Expand-Archive cmdlet introduced in PowerShell 5.0.

Measure-Command -Expression {
    Expand-Archive -Path 'C:\Users\tommymaynard\Desktop\HCM-920-UPD-018-WIN_1of10.zip' -OutputPath 'C:\Users\tommymaynard\Desktop\unzip\'
}

I executed the above command five times, too. The results were 2 minutes 11 seconds, 2 minutes 9 seconds, 2 minutes 10 seconds, 2 minutes 13 seconds, and 2 minutes 11 seconds.

This is a huge difference in time. Now, I do want to mention that I tested this on Windows 8.1 with PowerShell 5.1 (it’s in preview). The results may be better on different versions of Windows and with different versions of PowerShell. Let me know if you see different results with different configurations, and maybe I’ll do the same. The point is this, however: If you have a reason to speed up your project, you might consider .NET over a PowerShell cmdlet, or function. It seems I’m glad I did. Be sure you test different ways, to do the same thing.

Quick Learn – Get All 13 Months in a Year

I started a little, pet project recently and it’s all based around how I store my pictures and video files. I have a base directory – C:\Users\tommymaynard\Documents\Media – that contains a subdirectory for each year: 2012, 2013, etc. Inside each of those, I have 12 other directories – one for each month. These each use the naming convention 01 – January, 02 – February, etc. Inside each of those directories, I have at least two other directories: pictures and videos. Here’s an image that may help this structure make more sense.

Get All 13 Months in a Year01

I don’t always sort my photos and videos right away. Instead, I’ll often procrastinate and just drop them into a ‘Pics to Sort’ folder on my desktop – that’s literally the directory’s name. I wondered if I would procrastinate any less, if I had Windows PowerShell build out the desired directory structure for me each year. As an aside, if you consider that I always add my pictures and videos to the same I’ll-sort-you-later directory, then I could easily build a solution to do the sorting, based on file extension and creation date, from this directory into the proper subdirectories in the media directory. I’ll save this one for another day, but thanks for thinking of it.

For this Quick Learn, let’s only concern ourselves with capturing the month’s names. While I could have created a hard-coded array of the month names, I decided to see what I could get return using Get-Date – not what I wanted. I could return the current month’s name using Get-Date -Format MMMM, or the current month’s numeric representation using Get-Date | Select-Object Month, but neither of these return all of the months. The system knows the names of the months and so I opted to look for a .NET way to get them.

So, here it is, the System.Globalization.DateTimeFormatInfo .NET class. When you enter this as the value of the New-Object’s -TypeName parameter, it will return a great deal of information, to include a property called MonthNames. In the example below, you can see the code used to store the month names in a variable. You would think I would be done here, but I wasn’t.

PS C:\> $MonthNames = (New-Object System.Globalization.DateTimeFormatInfo).MonthNames
PS C:\> $MonthNames
January
February
March
April
May
June
July
August
September
October
November
December

PS C:\>

Instead of creating directories, like my project does, the example below simply echos the month’s numeric value, a space-dash-space, and then the month’s name. I’ve added a for statement example as well, just in case someone were to wonder why I didn’t include one, since I was incrementing a counter variable and knew how many looping iterations I had to do.

PS C:\> $i = 1
PS C:\> $MonthNames | foreach {Write-Output "$i - $_";$i++}
1 - January
2 - February
3 - March
4 - April
5 - May
6 - June
7 - July
8 - August
9 - September
10 - October
11 - November
12 - December
13 -
PS C:\>
PS C:\> for ($i=1; $i -le $MonthNames.Count; $i++) {echo "$i - $($MonthNames[$i-1])"}
1 - January
2 - February
3 - March
4 - April
5 - May
6 - June
7 - July
8 - August
9 - September
10 - October
11 - November
12 - December
13 -
PS C:\>

Based on these results, the MonthNames property has a thirteenth entry, and that’s a problem, since there’s no thirteenth month. I proved to myself that there was a problem a couple different ways, as seen below. Remember that the first index, or element, of an array is index zero, making January $MonthNames[0] and December $MonthNames[11].

PS C:\> $MonthNames.Count
13
PS C:\> $MonthNames[0]
January
PS C:\> $MonthNames[11]
December
PS C:\> $MonthNames[12]

PS C:\> $MonthNames[13]
PS C:\>

I needed to ensure this extra, blank month wasn’t making its way into my code. While I could have done my filtering when I was actually creating new directories, it is a better practice to only store what I need, and to only store what’s correct. As can be seen below, I only returned twelve months by adding a pipeline and some filtering.

PS C:\> $MonthNames = (New-Object System.Globalization.DateTimeFormatInfo).MonthNames | Where-Object -FilterScript {$_ -notlike ''}
PS C:\> $MonthNames
January
February
March
April
May
June
July
August
September
October
November
December
PS C:\> $MonthNames.Count
12

Since I was already here, I checked out another property. DayNames didn’t have a blank entry, and had I needed to use it, I wouldn’t have had to do any filtering on the returned results – something I incorrectly assumed about MonthNames. Hope this is helpful to someone, and thank you for reading this post.

PS C:\> $DayNames = (New-Object System.Globalization.DateTimeFormatInfo).DayNames
PS C:\> $DayNames
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
PS C:\> $DayNames.Count
7
PS C:\>