Tag Archives: Measure-Command

Explore the Command and Ensure the Efficiency

The best part of this entire process—the writing about PowerShell for coming up on nine years—has been the realization of my own mistakes while using PowerShell, and then sharing those. I have an upcoming post on this, but in between preparing and publishing that, I, yes me, made another mistake.

Let’s start with what I wrote first, which is included below. It’s a simple Get-ADComputer command. By now, we’ve all likely written plenty of these. Looks great right? Does it though?

Get-ADComputer -Filter * -Properties OperatingSystem,Description |
    Where-Object -Property OperatingSystem -like '*Server*' |
    Select-Object -Property Name, OperatingSystem |
    Sort-Object -Property OperatingSystem, Name

Before we move forward and determine what I did wrong, let’s use Measure-Command to see how long the execution takes for me on my machine.

Measure-Command -Expression {
    Get-ADComputer -Filter * -Properties OperatingSystem,Description |
        Where-Object -Property OperatingSystem -like '*Server*' |
        Select-Object -Property Name, OperatingSystem |
        Sort-Object -Property OperatingSystem, Name
}
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 4
Milliseconds      : 105
Ticks             : 41055467
TotalDays         : 4.75179016203704E-05
TotalHours        : 0.00114042963888889
TotalMinutes      : 0.0684257783333333
TotalSeconds      : 4.1055467
TotalMilliseconds : 4105.5467

Four seconds. That doesn’t seem too long, but I’m guessing it is based on the way in which the command was written. I never really trust a single test, so let’s set up the above Measure-Command command to run 10 times consecutively using ForEach-Object.

1..10 | Foreach-Object {
    Measure-Command -Expression {
        Get-ADComputer -Filter * -Properties OperatingSystem,Description |
            Where-Object -Property OperatingSystem -like '*Server*' |
            Select-Object -Property Name, OperatingSystem |
            Sort-Object -Property OperatingSystem, Name
    }
} | Select-Object -Property Seconds, Milliseconds
Seconds Milliseconds
------- ------------
      5          370
      5          213
      5          311
      5          839
      4          500
      6          234
      5           50
      5          239
      4          656
      5          421

Multiple tests and this is closer to a full five seconds per invocation. I don’t know about you, but five seconds in PowerShell is an eternity. We use PowerShell for accuracy, sure, but we use it for efficiency, as well. The quicker the better; we shouldn’t limit ourselves. If you find a mistake then fix it.

Now, let’s use the Filter parameter the way it was intended to be used. Piping to Where-Object is always going to be slower than using the filtering options provided by the commands we use. Here’s our base command corrected.

Get-ADComputer -Filter {OperatingSystem -like '*Server*'} -Properties OperatingSystem, Description |
    Select-Object -Property Name, OperatingSystem |
    Sort-Object -Property OperatingSystem, Name

And here it is wrapped up to be tested 10 times such as we did previously.

1..10 | Foreach-Object {
    Measure-Command -Expression {
        Get-ADComputer -Filter {OperatingSystem -like '*Server*'} -Properties OperatingSystem, Description |
            Select-Object -Property Name, OperatingSystem |
            Sort-Object -Property OperatingSystem, Name
    }
} | Select-Object -Property Seconds, Milliseconds
Seconds Milliseconds
------- ------------
      1          501
      0          922
      0          907
      0          895
      0          930
      0          906
      1          790
      1          534
      1          284
      0          937

Maybe you’re not worried about those lost seconds. If those aren’t that important, then let it be the possibility that someone’s going to see your failure to be efficient. If I saw this—and again I made this mistake first—I would be concerned that the person writing this code didn’t better explore the command(s) they’re using. If you’re going to use a command, then know it well enough to know how well you’re using it.

Build-in Measure-Command


Notice: The following post was originally published on another website. As the post is no longer accessible, it is being republished here on tommymaynard.com. The post was originally published on April 3, 2019.


I don’t know about you, but I have like five or six open tabs in my PowerShell editor at all times. Each was used to briefly prove something worked, or that it didn’t. Occasionally, these will become articles, but much more often, they just sit there for what feels like forever. I can’t seem to close some of these tabs, or even bring myself to save the sample code. If I can’t get to it now — or this week or month — what makes me think I’ll save it off and then get back to it? I probably won’t.

But today, I’m going to close a tab. This, right after I’ve written about it here. I’m not sure if it was supposed to become an article, but it’s going to regardless.

This tab’s single function is called Get-Date. It probably sounds familiar, and it should. Here’s the idea. I want a user to be able to invoke my function and return the date, just like the Get-Date cmdlet will do for them. Now, before we go any further, it’s important to understand that using Get-Date — the function — will absolutely remove any ability to use Get-Date — the cmdlet — in any one of the other 500 different ways it can be used. That’s okay. Remember, this came out of a tab with an unknown future. This isn’t fully thought out or tested, production-ready code. It’s an artist’s sketchbook or a student’s rough draft. I didn’t even have to use Get-Date. It could have been Get-ADUser or Set-Content. As you’ll see, the date portion of this function is inconsequential. It’s just what I chose when I needed something with which to test.

In addition to returning the standard Get-Date result, I also wanted to build in a way to measure the length of time it takes for my command to complete. This was the whole experiment; this was the purpose behind the tab. To do this, requires a user include the Measure switch parameter at the time of the function’s invocation. This measurement value isn’t displayed by default, but instead, it’s written to yes, a global variable, the user can inspect if they so desire. It still produces the Get-Date cmdlet’s default output, but now there’s more output waiting in the wings if it’s wanted.

In the first example, our Get-Date function executes as though we’ve run the default, Get-Date cmdlet. We have in fact. Without the Measure parameter, all we do is run the Get-Date cmdlet. When you look over the function’s code momentarily, you’ll see the fully-qualified, Get-Date command. A fully-qualified PowerShell command includes the module name. This is a requirement because we’ve named our function the same name as the cmdlet. Command precedence order indicates that functions execute before cmdlets if both commands have the same name.

In the second example, we’ll include the Measure switch parameter, and then return the value stored in the global, $Measurement variable. What I wanted to work, did. I built in a way to run the command and measure its time to complete if I want that information. As it’s just Get-Date, it doesn’t take long at all.

PS> Get-Date
Monday, April 2, 2019 10:24:06 PM
PS>
PS> Get-Date -Measure
Monday, April 2, 2019 10:24:07 PM
PS>
PS> $Measurement
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 1
Ticks : 10231
TotalDays : 1.18414351851852E-08
TotalHours : 2.84194444444444E-07
TotalMinutes : 1.70516666666667E-05
TotalSeconds : 0.0010231
TotalMilliseconds : 1.0231

Again, if this command is run with the Measure parameter, the length of time it took to complete is written to a global variable named $Measurement. As you’d expect, that variable will maintain its value until the variable name is reused for something else in the global scope, it’s overwritten by this function, it’s specifically removed, or the current PowerShell session has ended.

As can be seen in the below function, if the Measure parameter isn’t included, we run the fully-qualified, built-in Get-Date cmdlet. If it is included, we run Measure-Command against the fully-qualified, built-in Get-Date cmdlet. We use the OutVariable common parameter twice. We use it once with the Get-Date command, placing the current date and time into $CommandResult, and once with Measure-Command, placing the time to complete in our global $Measurement variable. We display $CommandResults and leave $Measurement in memory in case, the user opts to view the measurement results.

Function Get-Date {
    [CmdletBinding()]
    Param (
        [Parameter()]
        [switch]$Measure
    )
 
    If ($Measure) {
        Measure-Command -Expression {
            Microsoft.PowerShell.Utility\Get-Date -OutVariable CommandResult
        } -OutVariable Global:Measurement | Out-Null
        $CommandResult
    } Else {
        Microsoft.PowerShell.Utility\Get-Date
    } # If-Else.
} # End Function: Get-Date.

Before we wrap up, let’s look at a slightly modified version of the Get-Date function. This has been named Get-DateWithPause and hopefully, this will help indicate that the function is taking proper measurements. In this function, we’ve added two Start-Sleep commands where we pause for two seconds. Beneath this function, we’ll rerun our previous examples and spot those expected differences.

Function Get-DateWithPause { 
    [CmdletBinding()] 
    Param ( 
        [Parameter()] 
        [switch]$Measure 
    )
 
    If ($Measure) {
        Measure-Command -Expression { 
            Start-Sleep -Seconds 2 
            Microsoft.PowerShell.Utility\Get-Date -OutVariable CommandResult
        } -OutVariable Global:Measurement | Out-Null 
        $CommandResult 
    } Else { 
        Start-Sleep -Seconds 2 
        Microsoft.PowerShell.Utility\Get-Date 
    } # If-Else. 
} # End Function: Get-Date. 
PS> Get-DateWithPause
Monday, April 2, 2019 10:33:44 PM
PS>
PS> Get-DateWithPause -Measure
Monday, April 2, 2019 10:33:50 PM
PS>
PS> $Measurement
Days : 0
Hours : 0
Minutes : 0
Seconds : 2
Milliseconds : 2
Ticks : 20020567
TotalDays : 2.31719525462963E-05
TotalHours : 0.000556126861111111
TotalMinutes : 0.0333676116666667
TotalSeconds : 2.0020567

I’ve got a few more tabs with random pieces of PowerShell code. I’ll take a look through those, too. Just maybe I can close a few more tabs by giving their content a purpose here, even if they’re barely worthy. That said, I think this turned out alright. Someday maybe, I’ll get a measurement option builtin to all the functions I author.

Edit: In republishing this post here on tommymaynard.com, I decided to update the function a small bit. This version includes a second switch parameter (MeasureAndShow) that will both measure the command’s duration and display it at the same time, too. With more time, I might have implemented this differently, but this works.

Function Get-Date {
    [CmdletBinding()]
    Param (
        [Parameter()]
        [switch]$Measure,
		[Parameter()]
		[switch]$MeasureAndShow
    )

    If ($Measure -or $MeasureAndShow) {
        Measure-Command -Expression {
            Microsoft.PowerShell.Utility\Get-Date -OutVariable CommandResult
        } -OutVariable Global:Measurement | Out-Null
        $CommandResult
		if ($MeasureAndShow) {
			$Global:Measurement
		}
    } Else {
        Microsoft.PowerShell.Utility\Get-Date
    } # If-Else.
} # End Function: Get-Date.
[PS7.2.1][C:\] Get-Date

Sunday, February 13, 2022 8:17:48 AM

[PS7.2.1][C:\] Get-Date -Measure

Sunday, February 13, 2022 8:17:52 AM

[PS7.2.1][C:\] $Measurement

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 2
Ticks             : 25986
TotalDays         : 3.00763888888889E-08
TotalHours        : 7.21833333333333E-07
TotalMinutes      : 4.331E-05
TotalSeconds      : 0.0025986
TotalMilliseconds : 2.5986


[PS7.2.1][C:\] Get-Date -MeasureAndShow

Sunday, February 13, 2022 8:18:01 AM

Ticks             : 1781
Days              : 0
Hours             : 0
Milliseconds      : 0
Minutes           : 0
Seconds           : 0
TotalDays         : 2.06134259259259E-09
TotalHours        : 4.94722222222222E-08
TotalMilliseconds : 0.1781
TotalMinutes      : 2.96833333333333E-06
TotalSeconds      : 0.0001781

I am TechNet Gallery Years Old

I did not know it when I started, but it turns out, this is Part I.

Up until just recently, I had a section on my website called “TechNet Gallery.” It was right up there between “Contents” and “About.” I have an image of what it contained below. There was some pretty great stuff in there, but it was time for that section to be removed. With that, it is now time for the links to be updated, so people can find these scripts and modules again. The TechNet Gallery links no longer work. It has been a while since I have looked these over, but the links will likely be a combination of GitHub and the PowerShell Gallery. Check the content below the image for information about the first four entries. The others will be highlighted in additional, related posts.

1. “Get Synonyms for Approved and Unapproved Verbs”

I have always loved this script. I even used it yesterday, prior to knowing I would start piecing this post together. If there is a verb you want to use for your cmdlet or function, but it is not approved, this function will look for verb synonyms and tell you if they are approved or not. Here’s a quick image (because it is just beautiful).

While the 1.3 version is available in a GitHub Gist, the newest version — 1.4 — was written and uploaded to the PowerShell Gallery. You can either use that link or use PowerShellGet to download it using the PowerShell below. I have written about this script before, so you can read more here: https://tommymaynard.com/get-tmverbsynonym-1-4-2017/.

Install-Script -Name Get-TMVerbSynonym

2. “Active Directory User Lookup Form”

This was “my first, hand-coded PowerShell form using Windows Forms.” While it was available on the TechNet Gallery for download, that link no longer works.  Here is the 2015 post, and here is an updated link to the GitHub Gist. I always love doing a forms project and am grateful it made its way into .NET (core).

3. “Find DNS Servers Being Used by DHCP Scopes”

This script was written as a solution to a post on the TechNet Forums. I did not read that whole thread, but I can offer you the link to the Gist if this is helpful or interesting. Oh, one other thing, I apparently wrote about the script here on my own site, as well. All the way back in 2015!

4. “Measure Command with Multiple Commands, Repetitions, Calculated Averages”

There is no way I did not write about this one. I loved this script and to this very day, I still think it should be implemented in PowerShell by the PowerShell Team. Perhaps I will add it as a discussion in GitHub in the future. I do not think that was a thing back in 2017. Here is my post about it now!

Measure-Command can only measure one command and only one time per command invocation. This changed that. It allowed a user to measure multiple commands multiple times. It could even calculate the average time for a command to complete. I think its Command parameter accepted a string, so that should probably be changed to a ScriptBlock.

Here is an old image that accompanies the above link to the article on my site — click it to enlarge it. And here, is a link to the code in a Gist.

There is more to cover, but we will put that on hold for a moment. I will be back with a continuation of these soon. And once I am, the below text will link to Part II of this series!

Part II

Saving Time with Background Jobs

If you’re like me, there’s something you know a decent amount about regarding PowerShell, but you just don’t get to use it much. Today, it’s PowerShell background jobs. If you’ve been reading my blog currently, then you know I’m right in the middle of a series regarding Splunk. In the series, I’m sending telemetry data from my function template to Splunk. The problem, although slight, is that it’s increased the duration, or length of time, the function takes to complete. No surprise. It’s running several additional commands where it polls the user and system for information. It’s only adding maybe a second more of time to the duration of the function. Still, why not determine if it’s time that can be reclaimed. Enter background jobs.

If I can collect my telemetry data in the background, while the function is doing whatever it’s supposed to be doing, then I can potentially remove any additional time added to the invocation of the function due to collecting telemetry data. Let’s take a look a couple code examples to begin.

This first function is called Start-SleepWITHOUTBackgroundJob. Notice the “without” in the name. This function will run Start-Sleep twice within the function: once for five seconds and then once for three seconds. Therefore, we’d expect the function to take around eight seconds to complete. The five second section is standing in for where we’d run our standard function code. The three second section is standing in for where we’d collect our telemetry data.

Function Start-SleepWITHOUTBackgroundJob {
    Start-Sleep -Seconds 5

    Start-Sleep -Seconds 3
} # End Function: Start-SleepWITHOUTBackgroundJob.

Measure-Command -Expression {
    Start-SleepWITHOUTBackgroundJob
}

Let’s run it a few times. As you’ll see, and just as we’d suspected, it comes in at right around the 8 second mark. If you’ve seen the output of Measure-Command then you can tell I’ve removed several of the time properties; they weren’t necessary.

Seconds           : 8
Milliseconds      : 16

Seconds           : 8
Milliseconds      : 26

Seconds           : 8
Milliseconds      : 22

The second function is called Start-SleepWITHBackgroundJob. We’ve swapped our Start-Sleep commands because we want what takes less time to happen first. It has to be what happens inside the background job. I suspect that gathering telemetry data is most always going to take less time than whatever else the function is doing. That may not always be the case, but it’s a safe choice.

Function Start-SleepWITHBackgroundJob {
    Start-Job -ScriptBlock {
        Start-Sleep -Seconds 3
    } | Out-Null

    Start-Sleep -Seconds 5
} # End Function: Start-SleepWITHBackgroundJob.

Get-Job | Remove-Job
Measure-Command -Expression {
    Start-SleepWITHBackgroundJob
}

And, look at that. We’ve shaved off three seconds from our function invocation by placing those three seconds inside of a background job. Our three seconds are running in a separate PowerShell process that executes at the same time the function sleeps for five seconds. This is going to work great for me.

Seconds           : 5
Milliseconds      : 596

Seconds           : 5
Milliseconds      : 795

Seconds           : 5  
Milliseconds      : 417

Now that we’ve proved we can use PowerShell background jobs to save time and avoid some unnecessary top-to-bottom/sequential programming, let’s do this while actually gathering some telemetry data. We’ll do two things at once and shave off some time from the overall time. The time difference may not be as dramatic as the above examples, but I’ll take anything. In fact, watch this first.

Do you see the delay? There’s a moment where my telemetry data is being gathered and sent to Splunk, before the prompt reappears. The idea is to get those milliseconds back — they add up!

As you can see below, we have another code example. This will run without a background job. It’ll sleep for five seconds (as thought it’s fulfilling its purpose), and then collect some telemetry data and display that on the screen. I’ll share the code in between each of the below regions at the end of this post in case someone finds themself interested.

Function Start-SleepWITHOUTBackgroundJob {
    Start-Sleep -Seconds 5

    #region: Obtain telemetry.
	New-Variable -Name FuncTmplHash -Value @{}
	New-Variable -Name TelemetryHashBonus -Value @{}
        #region: Determine PowerShell version.
        #endregion.
        #region: Check for other version: Windows PowerShell|PowerShell.
        #endregion.
        #region: Determine IP address(es).
        #endregion.
        #region: Determine Operating System.
        #endregion.
        #region: Determine computer tier.
        #endregion.
    $TelemetryHashBonus
    #endregion.
} # End Function: Start-SleepWITHOUTBackgroundJob.

Measure-Command -Expression {
    Start-SleepWITHOUTBackgroundJob | Out-Default
}

While the time difference isn’t too dramatic (roughly 750 milliseconds), it’s something. Something of which I want to partially reclaim. This is exactly why you see the hesitation/pause before PowerShell rewrites a fresh prompt in the above GIF. Now, let’s get this corrected.

Function Start-SleepWITHBackgroundJob {
    Start-Job -ScriptBlock {
        #region: Obtain telemetry.
        New-Variable -Name FuncTmplHash -Value @{}
        New-Variable -Name TelemetryHashBonus -Value @{}
        #region: Determine PowerShell version.
        #endregion.
        #region: Check for other version: Windows PowerShell|PowerShell.
        #endregion.
        #region: Determine IP address(es).
        #endregion.
        #region: Determine Operating System.
        #endregion.
        #region: Determine computer tier.
        #endregion.
        $TelemetryHashBonus
        #endregion.
     } -OutVariable Job | Out-Null

    Start-Sleep -Seconds 5
    Receive-Job -Id $Job.Id
} # End Function: Start-SleepWITHBackgroundJob.

Measure-Command -Expression {
    Start-SleepWITHBackgroundJob | Out-Default
}

If we take a look a the below results versus the run without the background job we can see that we’ve saved roughly 500 milliseconds, or a 1/2 a second. That’s not much; I’d agree, even though it feels like an eternity when I’m waiting for my prompt to be rewritten. I guess I should consider that this isn’t the full telemetry gathering code I use. Still, for every two invocations, I save a single second. One hundred and twenty invocations saves me a minute. If my tools are far reaching, then there’s definitely time to be saved.

It does take time to create the job and receive its data once it’s complete, so perhaps that’s eating into my return on time, as well. That makes me think of one more thing worth sharing. If you find yourself interested in implementing something like this, then it’s probably wise to not assume the background job is complete, as I’ve done in these examples. Instead of running Receive-Job, first run Get-Job and ensure your job’s State property is “Completed,” and not still “Running.” It would probably be best to put this inside a Do-Until language construct, so it can loop until you can be certain the job is completed, before receiving its data.

I said I share the telemetry gathering code, so that’s been included below. I make no guarantees that it’ll work or make sense for you, but there it is.

#region: Obtain telemetry.
New-Variable -Name FuncTmplHash -Value @{}
New-Variable -Name TelemetryHashBonus -Value @{}

#region: Determine PowerShell version.
$FuncTmplHash.Add('PSVersion',"$(If ($PSVersionTable.PSVersion.Major -lt 6) {"Windows PowerShell $($PSVersionTable.PSVersion.ToString())"} Else {
	"PowerShell $($PSVersionTable.PSVersion.ToString())"})")
$TelemetryHashBonus.Add('PSVersion',$FuncTmplHash.PSVersion)
#endregion.

#region: Check for other version: Windows PowerShell|PowerShell.
If ($FuncTmplHash.PSVersion -like 'PowerShell*') {
	$TelemetryHashBonus.Add('PSVersionAdditional',
		"$(try {powershell.exe -NoLogo -NoProfile -Command {"Windows PowerShell $($PSVersionTable.PSVersion.ToString())"}} catch {})")
} ElseIf ($FuncTmplHash.PSVersion -like 'Windows PowerShell*') {
	$TelemetryHashBonus.Add('PSVersionAdditional',
		"$(try {pwsh.exe -NoLogo -NoProfile -Command {"PowerShell $($PSVersionTable.PSVersion.ToString())"}} catch {})")
} # End If-Else.
#endregion.

#region: Determine IP address(es).
$ProgressPreference = 'SilentlyContinue'
$TelemetryHashBonus.Add('IPAddress',(Invoke-WebRequest -Uri 'http://checkip.dyndns.com' -Verbose:$false).Content -replace "[^\d\.]")
$TelemetryHashBonus.Add('IPAddressAdditional',@(Get-NetIPAddress | Where-Object -Property AddressFamily -eq 'IPv4' |
	Where-Object -FilterScript {$_ -notlike '169.*' -and $_ -notlike '127.*'}).IPAddress)
$ProgressPreference = 'Continue'
#endregion.

#region: Determine Operating System.
If ($FuncTmplHash.PSVersion -like 'Windows PowerShell*' -and $FuncTmplHash.PSVersion.Split(' ')[-1] -lt 6) {
	$TelemetryHashBonus.Add('OperatingSystem',"Microsoft Windows $((Get-CimInstance -ClassName Win32_OperatingSystem -Verbose:$false).Version)")
	$TelemetryHashBonus.Add('OperatingSystemPlatform','Win32NT') 
} Else {$TelemetryHashBonus.Add('OperatingSystem',"$($PSVersionTable.OS)")
	$TelemetryHashBonus.Add('OperatingSystemPlatform',"$($PSVersionTable.Platform)")} # End If-Else.
#endregion.

#region: Determine computer tier.
Switch ($FuncTmplHash.'Domain\Computer') {{$_ -like '*-PT0-*'} {$TelemetryHashBonus.Add('ComputerTier','T0'); break} 
{$_ -like '*-PT1-*'} {$TelemetryHashBonus.Add('ComputerTier','T1'); break}
default {$TelemetryHashBonus.Add('ComputerTier','Unknown')}} # End Switch.
#endregion.
$TelemetryHashBonus
#endregion.

Add Measure-Command into a Function

During an at work demonstration of my Advanced Function Template recently, I heard an interesting idea from a teammate. It all started when I mentioned how many lines of code my function template required, without having even added any non template code to it. In version 1.0, the template required around 75 lines, which felt high. In subsequent versions it crept up past 100 — maybe 120. That also felt high, until I ran some tests and stopped caring. It’s probably past 120 by now, but I’ve stopped paying attention, as it no longer matters to me.

In my testing, a function that includes only a single string has an execution time of around 5,000 ticks. With 10,000 ticks per millisecond, you’re right to think that’s fast. With my most current version of the Advanced Function Template, with no additional code, it comes in at 3 – 4 milliseconds. Personally, I can’t tell the difference between a 1/2 of a millisecond and a few, and so the number of lines required by the template stopped meaning much to me. Especially since it only includes relevant and necessary code anyway. It’s worth it, especially since we now have region support in Visual Studio Code and can collapse any code we don’t want to have to constantly view anyway.

The interesting idea that was brought up, was to add a way to measure the execution of the Advanced Function Template (and its included, user based code [once that’s been added]), from within the function that’s been built using the template itself. I had never thought of such a thing. This is to say, wrap the Measure-Command cmdlet inside the template, in order to be able to run it against itself. If that’s confusing, no worries, the following code example should help explain.

Function Get-TheAlias {
    [CmdletBinding()]
    Param (
        [string]$Definition,
        [switch]$Measure
    )
    
    If ($Measure) {
        [System.Void]($PSBoundParameters.Remove('Measure'))
        Measure-Command -Expression {Get-TheAlias @PSBoundParameters}
    } Else {
        Get-Alias @PSBoundParameters
    }
}

The first two below examples do not use the Measure switch parameter offered in the above function. Therefore, they’ll head down the Else path of the If-Else statement. The first example will internally run Get-Alias with no parameters or parameter values (not all results are displayed). The second example also uses Get-Alias internally, however, it does so using the Definition parameter and a parameter value. The Definition parameter allows us to determine if there’s an alias, or aliases, for the given cmdlet or function. As there is, an alias for Get-Command is displayed.

PS > Get-TheAlias

CommandType     Name                                Version    Source
-----------     ----                                -------    ------
Alias           % -> ForEach-Object
Alias           ? -> Where-Object
Alias           ?: -> Invoke-Ternary                3.2.0.0    Pscx
Alias           ?? -> Invoke-NullCoalescing         3.2.0.0    Pscx
Alias           ac -> Add-Content
Alias           And -> GherkinStep                  4.1.0      Pester
...
Alias           gwmi -> Get-WmiObject
Alias           h -> Get-History
Alias           hib -> Suspend-Computer
Alias           hibernate -> Suspend-Computer
Alias           history -> Get-History
Alias           i -> powershell_ise.exe

PS > Get-TheAlias -Definition Get-Command

CommandType     Name                                Version    Source
-----------     ----                                -------    ------
Alias           gcm -> Get-Command

The next two examples of the Get-TheAlias function do use the Measure parameter. In the first example, we only use the Measure parameter. Because the code dictates, we’ll head down the If portion of our If-Else language construct. It will strip the Measure parameter, and then measure the time it take to run a second, internal instance* of the Get-TheAlias function (which again, simply runs Get-Alias). When we also include the Definition parameter and a value, we’ll again strip the Measure parameter, and internally run a measured copy of Get-TheAlias function. This will again, execute Get-Alias with the Definition parameter and the submitted parameter value.

* Update: I do want to mention here, that there’s really no internal instance of the function. When a function calls itself, it looks for the function in it’s own scope (the child scope, inside the function). When it cannot be located there, it goes upward, to the parent scope, where it will find the declared function. Maybe this helps.

PS > Get-TheAlias -Measure

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 0
Ticks             : 6681
TotalDays         : 1.75694444444444E-08
TotalHours        : 4.21666666666667E-07
TotalMinutes      : 2.53E-05
TotalSeconds      : 0.001518
TotalMilliseconds : 1.518

PS > Get-TheAlias -Definition Get-Command -Measure

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 1
Ticks             : 10287
TotalDays         : 7.28819444444444E-09
TotalHours        : 1.74916666666667E-07
TotalMinutes      : 1.0495E-05
TotalSeconds      : 0.0006297
TotalMilliseconds : 0.6297

It’s weird and different sure, but most of all, I need some thoughts on whether or not this will actually work long term. In my mind it does. It tries to run a function from its own function scope, when it can find that function, it goes up to the global scope and runs itself without the measure switch parameter. Sorry, if you’re still confused, but I’m finding it difficult to be 100% pleased with my ability to describe things.

If you’re not confused by this, then walk thought the function a time or two in your own head, or maybe even on a computer! Can you create or think of scenarios where adding a way to measure how long a function takes to complete can’t, or shouldn’t, be added to itself?

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.

Proving PowerShell’s Usefulness to Newbies, Part I

Months ago, I started writing a list of ways to potentially impress PowerShell newbies for a presentation (I’ve yet to ever give). Since I’m not sure if I’ll be up in front of a crowd of PSNewbies any time soon, I figured I would share them here. Consider these when you need to impress the PowerShell non-believers.

Part I
The power in PowerShell has been said to be a number of different things, by a number of different people. In today’s topic, the power is speed and accuracy. We’re going to automate the creation of 10,000 directories (a.k.a. folders). That’s no small task if you’re doing it manually.

Beginning in the example below, we’ll start by creating a single directory to hold the other 10,000 directories, by running a New-Item command. Once that’s finished, which is practically instantaneous, we’ll run second command to create the 10,000 new folders. We’ll do this by using a range operator (..) and the numbers 1 and 10000, piping each of those numbers and the ones in between, to that second New-Item command.

Notice the $_ variable, as this has the tendency to cause some confusion. This variable holds the value of the current object that’s entered, or crossed, the pipeline — you pick your visual. In this case, it represents the current number from within our range of numbers. The first time this command runs, $_ will be set to 1, the second time this runs it will be set to 2, and the last time it runs, it will be set to 10000. I should note, that in PowerShell 3.0, the $PSItem variable was introduced. It can be used in place of $_ in version 3.0 and above.

PS> New-Item -Path C:\Testing -ItemType Directory

    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----          1/6/2016   9:52 PM            Testing

PS> 1..10000 | ForEach-Object {New-Item -Path "C:\Testing\$_" -ItemType Directory}

    Directory: C:\Testing

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----          1/6/2016   9:52 PM            1
d----          1/6/2016   9:52 PM            2
d----          1/6/2016   9:52 PM            3
d----          1/6/2016   9:52 PM            4
d----          1/6/2016   9:52 PM            5
d----          1/6/2016   9:52 PM            6
d----          1/6/2016   9:52 PM            7
d----          1/6/2016   9:52 PM            8
d----          1/6/2016   9:52 PM            9
d----          1/6/2016   9:52 PM            10
...

I haven’t included all 10,000 results, for good reason, but as you can see, the moment the command was entered into the PowerShell host, it began to create the new folders. This would be the time at which you retie your shoes. The example further below indicates the command takes about 10 seconds to complete. Depending on your sneakers, if you’re wearing those, you might actually take longer. If you don’t have laces, take a drink of something, or simply sit there in amazement at all the work you’re not really doing. It might actually be too fast, once you’ve realized how much time you just freed up.

A little note about Measure-Command used below: Typically this cmdlet will internally measure things and not really do much that you can actually see. In this case, it’s actually going to create the folders as part of the measurement. To me, it makes sense that it wouldn’t be able to measure this unless it really creates the folders. On that note, if you ran the command above, you’ll want to delete the existing folders, if you want to measure how long it takes to create the folders. The New-Item cmdlet can’t create folders that already exist. To remove the directories, run:  Remove-Item -Path ‘C:\Testing\*’ from your PowerShell console.

PS> Measure-Command -Expression {1..10000 | ForEach-Object {New-Item -Path "C:\Testing\$_" -ItemType Directory}}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 10
Milliseconds      : 293
Ticks             : 102939103
TotalDays         : 0.000119142480324074
TotalHours        : 0.00285941952777778
TotalMinutes      : 0.171565171666667
TotalSeconds      : 10.2939103
TotalMilliseconds : 10293.9103

PS> Remove-Item -Path C:\Testing\*

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 9
Milliseconds      : 793
Ticks             : 97934962
TotalDays         : 0.000113350650462963
TotalHours        : 0.00272041561111111
TotalMinutes      : 0.163224936666667
TotalSeconds      : 9.7934962
TotalMilliseconds : 9793.4962

In the second command above, we actually measured the time it took to remove the directories we created — you saw that command a moment ago. It came in at a faster time than it took to create them, although this won’t always be the case. It’s still quite quick for either operation.

This is a great example of the power in PowerShell, but before I wrap up, let’s see how much time I just saved. Let’s assume it takes me 4 seconds to manually create a folder. To create 10,000 folders, I would need a free 40,000 seconds. To put that into hours, I would need over 11 of them to do this task by hand. We only get one person’s salary, but we should probably get a few.

PS> $SecondsToCreateFolder = 4
PS> $NumberOfFolderToCreate = 10000
PS> $NumberOfFolderToCreate * $SecondsToCreateFolder
40000
PS> $SecondsInMinutes = $MinutesInHours = 60
PS> $SecondsInMinutes
60
PS> $MinutesInHours
60
PS> ($NumberOfFolderToCreate * $SecondsToCreateFolder) / $SecondsInMinutes / $MinutesInHours
11.1111111111111

Thanks for reading this post.

An Improved Measure-Command: Multiple Commands, Multiple Repetitions, Calculated Averages, and Pauses Between Runs

Download the Measure-TMCommand function here: https://gist.github.com/tommymaynard/c97c5248d76aba08f1c8aa01096aa12b

In Windows PowerShell, there are often several ways to complete the same task. With that in mind, it makes sense that we might want to determine how long commands and scripts take to complete. Until now, Measure-Command has been the cmdlet we’ve used.

While Measure-Command has been helpful, I’ve often thought it should include some additional functionality. Therefore, I’ve written an advanced function, Measure-TMCommand, that adds all the benefits listed below:

– Continually measure the execution time of a single command and/or script, up to a user-defined number of repetitions.

– Continually measure the execution time of multiple commands and/or scripts, up to a user-defined number of repetitions.

– Calculate the average time a command(s), and/or a script(s)  takes to execute.

– Display limited hardware information about the computer where the command and/or script is being measured.

– Includes an option to display the output of the command and/or script, as well as the measurement results.

Updated 4/15/2015 (v1.2.1): Added a parameter -TimeInBetweenSeconds with a parameter alias of -Pause. This will pause the function between executions, allowing the ability to test at different times between a set time. For instance, let’s say you want to measure a command every 1/2 hour for six hours: 12 repetitions with 30-minute pauses. You would then run the command with the -Repetitions parameter with a value of 12 and the -TimeInBetweenSeconds (or -Pause) with a value of 1800 (as in 1800 seconds, or 30 minutes).

Here’s the function in action:

Measure-TMCommand1.2.1

In the example, above, we can easily determine that using the -Name parameter of the Get-Service cmdlet is faster than piping the entire result set to the Where-Object cmdlet, and then filtering on the name. Notice that not all properties were returned — only the ones in which I was interested.

With the addition of the -TimeBetweenInSeconds, or -Pause, parameter I have considered that this function might be better served to also have an -AsJob parameter. I’ll look into it, but no promises. Thanks, and enjoy.

Clear-Host, Without Clearing the Host

After you read this, read part 2 (and download the TMModule)

I use the Clear-Host cmdlet alias, cls, throughout the day to clear out whatever typing I have inside my Windows PowerShell console. It does its job well, but recently I’ve wanted it to work differently. I wanted it to appear that the console host has been cleared, but still allow me to scroll back up to see what was on the screen before it was cleared. I started playing around with the console class, [Console]. While not necessary, this can also be written using the namespace, System, such as [System.Console]. I like the idea of being as complete as possible and so you’ll see me use the namespace even though it’s not necessary.

Before I could write something reusable, such as a function, I had to figure out if what I wanted to accomplish, was even possible. I knew I was working with [System.Console] and so I piped that to Get-Member, but it returned the methods and properties of System.RuntimeType, seen below.

PS C:\> [System.Console] | Get-Member

    TypeName: System.RuntimeType

I struggled for a moment until I remembered an article I had read on using static classes. I found that page again, http://technet.microsoft.com/en-us/library/dd347632.aspx, and was quickly reminded that using the -Static parameter of the Get-Member cmdlet would get me the correct results.

PS C:\> [System.Console] | Get-Member -Static

    TypeName: System.Console

Running the command above produces the TypeName as shown, but it also produces all the methods and properties. I started looking over the properties and a couple about the cursor quickly caught my eye, especially the CursorTop property. After the Get-Member command from above, and based on the results of returning the CursorTop property, my cursor was positioned at line 59 inside my console, as can been seen in the example below on line 2. I cleared the screen, and beginning on line 4 below, I reran the command three more times. Each time, it gave me the location where the cursor was last positioned.

PS C:\> [System.Console]::CursorTop
59
PS C:\> cls
PS C:\> [System.Console]::CursorTop
1
PS C:\> [System.Console]::CursorTop
3
PS C:\> [System.Console]::CursorTop
5
PS C:\>

I decided I would assign the value, 0, to the CursorTop property and suddenly I was writing over the text on the top line. Take a close look at line 1 below.

PS C:\> blahblahConsole]::CursorTop
59
PS C:\> cls
PS C:\> [System.Console]::CursorTop
1
PS C:\> [System.Console]::CursorTop
3
PS C:\> [System.Console]::CursorTop
5
PS C:\> [System.Console]::CursorTop = 0

I could move my cursor, great, but this wasn’t exactly what I wanted. What I wanted was to push that scroll bar down so that anything that was already on the screen was pushed off the top of my console, and all that was left was my PowerShell prompt. I still believed there was a way to do this and so I spent a little more time looking over the properties. I found four that began with Window – WindowHeight, WindowLeft, WindowTop, and WindowWidth – and began to experiment with them. I didn’t suspect I’d be doing anything with the height and width but I thought I would check out their values anyway – 50 and 120, respectively.

PS C:\> [System.Console]::WindowHeight
50
PS C:\> [System.Console]::WindowWidth
120
PS C:\>

WindowLeft didn’t seem to be that important, because no matter how much was typed before I entered [System.Console]::WindowLeft, the property value was still set to 0. Then I entered in [System.Console]::WindowTop and it was also 0 every time. Then it dawned on me, what if I changed its value like I did with CursorTop. I tried it and my scroll bar started jumping all over. I’m getting close!

PS C:\> [System.Console]::WindowTop = 10
PS C:\> [System.Console]::WindowTop = 200
PS C:\> [System.Console]::WindowTop = 2000
PS C:\> [System.Console]::WindowTop = 0

We know the CursorTop value changes, so what would happen if we set the value of WindowTop to CursorTop? I tried it, and it worked!

PS C:\> [System.Console]::WindowTop = [System.Console]::CursorTop
PS C:\>

I thought I was done when I took another moment and scanned over the methods. I found one called SetWindowPosition. Instead of simply assigning a new value to the property WindowTop, I decided I would use the method to do the work for me. I eventually ran both of these options through the Measure-Command cmdlet and determined that there was no gain in speed by using one option over the other.

So, once I knew what to do, I opened my profile ($PROFILE) and created an empty function. For whatever reason, I called it clx thinking that this would be a good option for me. Turns out that while clx has little meaning, I was able to quickly remember it and start using it right away. Now, every time I want it to appear that I’ve cleared my host, but didn’t really, I type clx and press Enter.

Function clx {
    [System.Console]::SetWindowPosition(0,[System.Console]::CursorTop)
}

I added one additional feature to this function as is seen in the example below. This option allowed me to run the clx function and leave the last n number of rows on the screen. Try it out by ensuring your have some output in your console and then entering clx 2. This will “clear” the console screen but still allow you to view the last two rows without scrolling back up. Try it and it may make more sense.

Function clx($SaveRows) {
    If ($SaveRows) {
        [System.Console]::SetWindowPosition(0,[System.Console]::CursorTop-($SaveRows+1))
    } Else {
        [System.Console]::SetWindowPosition(0,[System.Console]::CursorTop)
   }
}

Here’s a video of the function in action. The first thing we do is return 5 processes and then 5 services. Then we use cls and notice that we cannot scroll back up to see what was cleared. This is the typical behavior. When we add the processes and services back, and then use the clx function, we can see that we have the option to scroll back up and see what was on the screen, before we “cleared” it.