Quick-Hits: How Long Did That Last Command Take?

Have you ever ran a command or script and wanted to know how long it takes to complete but forgot to wrap the code or script with Measure-Command? Well, you are in luck because you can take a look at the history using Get-History and check out the StartExecutionTime and EndExecutionTime to determine how long it took the command to run!

Lets run a simple command such as Get-ChildItem on my C:\Windows\System32 directory.

Get-ChildItem C:\Windows\System32

Ok, now that we have done that, we need to use Get-History to get the time it took to complete the command by subtracting StartExecutionTime from EndExecutionTime to get that answer.

(Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime

image

As you can see, the command took just over 9 seconds to run. Now this isn’t a replacement for Measure-Command, but it does provide a decent way to find out how long a command took to run. The [-1] that I used means that I am getting the last member of the collection.

Pretty cool stuff, but I wanted an easier way to get this information without having to type out that command. So naturally, I made this into a simple function that you can use below.

Code

With this function, you can simply run it after a command to find out how long the last command took to run.

Function Get-LastCommandExecutionTime {
    <#
        .SYNOPSIS
            Gets the execution time of the last command used.
        .EXAMPLE
            Get-LastCommandExecutionTime

            Description
            -----------
            Gets the last execution time of the last command run.
    #>
    Process {
        (Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime
    }
}
Get-Command
Get-LastCommandExecutionTime

image

Nothing revolutionary, but still something that you might find useful in your scripting journeys.

Enjoy!

Posted in powershell | Tagged , , | 1 Comment

Filter Using Parameters Instead of Where-Object When Possible

This was a post I intended on writing during/after the 2012 Scripting Games, but just never got around to finishing it up. While it is great to know for the games itself, it also applies to everyday scenarios for folks just coming up into PowerShell and for those that have been doing this for a while.

When writing a one-liner or a script in PowerShell, the recommended practice is to filter as far left as possible before doing anything else to bring down the amount of data to be as small as possible. Typically we use the following process:

Filter | Select/Sort | Format

When writing a script or a function it is important to know the capabilities of existing cmdlets, in this case whether it has the ability to filter for a specific item. And I don’t mean that it has just a –Filter parameter, but other types of parameters that can be used to filter the data being returned. A lot of times the old standby is to take the results of a cmdlet and pipe it into Where-Object to filter for the data that we want.

Most of the time, this is the way to get what you need, but it is also very beneficial to you to spend a short time looking at the parameters on a specific cmdlet just to see if there is something that will help you to get the data you need. While the time difference may not always be that great, it does help the code by taking out the un-needed Where-Object filter. Also it is important to note that times will vary on your system based on resources, bandwidth, etc… as well as what it is you are trying to filter. In other words, you may not see astronomical differences in some commands that you might see in others. Regardless, it still a good practice to get into using what the cmdlet has to offer as it is using the OS APIs to handle the filtering which is especially useful when run against remote systems as all of the filtering is happening on the remote end rather than having to bring all of the data back. On top of bringing all of that data back, you are then using the local system resources for filtering.

Examples

For instance, lets look at using Get-WMIObject with both a Where-Object filter and then using the same command but instead using the builtin –Filter parameter against a simulated 25 remote systems (by simulated, I mean the same remote system 25 times).

For the sake of getting command times, every command will be wrapped in Measure-Command {}.

 Measure-Command {
    1..25 | ForEach {
         Get-WmiObject -Class Win32_Service -ComputerName DC1 | Where {
            $_.State -eq 'running'
         }
    }
}

 

image

13 seconds against 25 systems…not too bad, but lets see what happens with the –Filter parameter.

Measure-Command {
    1..25 | ForEach {
        Get-WmiObject -Class Win32_Service -ComputerName DC1 -Filter "State='running'"
    }
}

image

Now that is better. It went from 13 seconds down to 6 seconds by using the built-in –Filter parameter.

What is happening here is that where the Where-Object will take each complete collection of objects from the remote system and then does the filtering, which is time consuming and a waste of resources. The –Filter will actually perform all of the filtering that is provided on the remote system before bringing the data across the network to the console. Much more efficient which saves on time and resources.

Let’s take a look at another example, this time using Get-ChildItem to show why you might want to check out the parameters prior to filtering with Where-Object for specific files or extensions.

Measure-Command {
    Get-ChildItem -Path C:\ -Recurse -ErrorAction SilentlyContinue | Where {
        $_.Extension -eq "txt"
    }
}

image

Ok, not too bad with using Where-Object to filter for only .TXT files, but again, lets apply the –Filter parameter to see how much of a jump in performance we can get.

Measure-Command {
    Get-ChildItem -Path C:\ -Recurse -ErrorAction SilentlyContinue -Filter "*.txt"
}

image

Wow, over a minute difference between between the –Filter parameter being used and using Where-Object to filter the objects for .TXT files. So in this case, we can easily see how much better using a parameter filter is better than piping the objects to Where-Object for filtering.

The last example of this article is looking at Get-WinEvent and using the built-in filter parameters are quicker than using Where-Object.

Measure-Command -Expression {
    Get-WinEvent -LogName application -ErrorAction SilentlyContinue | Where-Object { 
        $_.providername -Like '*msi*' -AND 
        $_.TimeCreated -gt (Get-Date).AddDays(-15) 
    }
}

image

Now for the same cmdlet with some filtering.

Measure-Command {
     Get-WinEvent -ea SilentlyContinue -FilterHashtable @{
        ProviderName= "MsiInstaller"
        LogName = "application"
        StartTime = (Get-Date).AddDays(-15)
    }
}

image

And once again we can see that the built-in filtering with parameters out performs using Where-Object.

As we have seen, sometimes the time difference may not be that great, but other times it is more than enough to show why you want to spend a little time to find out what types of parameters that a cmdlet has. By doing this, you can potentially find a more efficient way of gathering data, especially when you start stacking multiple commands against multiple remote systems. If you remember earlier I mentioned about Filter | Select/Sort | Format, you could probably break it up a little more to be Parameter Filter | Where-Object Filter | Select/Sort | Format.

Posted in powershell | Tagged , , , | 7 Comments

PowerShell Magazine Article Live Today

My first ever article on the PowerShell Magazine is now live today! This article talks about my favorite 3 tips for PowerShell. This is a great opportunity that I could not pass up on! Check it out and let me know what you think!

Boe Prox’ Favorite PowerShell Tips & Tricks

Posted in News, powershell | Tagged , | Leave a comment

Tech-Ed 2012 Bound

In a few more days I will be heading out to Tech-Ed 2012 to check out some great sessions and meet up with some amazing people. I last went to Tech-Ed back in 2010 when it was in New Orleans and had a blast. Did I mention I won a pass from the Scripting Games by placing 2nd? Well, that time was amazing and my brain was pretty full by the time it was all said and done.

This year the conference is being held in Orlando,FL and promises to be just as good as the one I went to a couple years ago. My schedule is packed with sessions dealing with mostly PowerShell (of course) as well as other sessions dealing with DNS and Active Directory as well as checking out the Tech Expo and visiting various vendors and Microsoft experts. I will of course be checking out the PowerShell and Scripting Guy booth as well. There is also a post Tech-Ed PowerShell workshop that deals with a base Windows Server 2012 image and works with the following items:

  • Writing a PowerShell script workflow to perform Server deployments
  • Creating a constrained endpoint that hosts only the deployment workflow
  • Delegate a set of credentials for the workflow to use
  • Exposing the workflow and it’s results through a RESTful webservice
  • Using Windows PowerShell Web Access to manage the workflow

Yes, it is going to be that awesome!

For those of you attending, I will see you there!

Posted in News, Tech-Ed | Tagged , | Leave a comment

Working With New CmdletBinding Arguments HelpURI,SupportsPaging and PositionalBinding In PowerShell V3

With PowerShell V3 out (still in Beta), there are some new things with the CmdletBinding that includes the following items:

  1. HelpURI
  2. SupportsPaging
  3. PositionalBinding

Each of these has their own specific uses and I will dive into each one to show you an example for each of these and give a explanation. Still available are the other items such as SupportsShouldProcess, DefaultParameterSetName and ConfirmImpact, but I will not be going into these today.

HelpURI

The HelpURI basically allows you to specify a URI (Uniform resource identifier) that can be used to point to more help documentation about the cmdlet/function by using the following command:

Get-Help <function> –Online

Note that this is an internet address and will open up a web browser to go to the specified location. If you have Comment Based help added (and why wouldn’t you Smile ) and have the .Link specified with an URI for help, then it will take precedence to whatever is being used with the cmdletbinding HelpURI.

First lets look at using this new feature with just the HelpURI specified:

Function Get-Something {
    [CmdletBinding(
        HelpURI='http://google.com'
    )]
    Param (
        [parameter(ValueFromPipeLine=$True)]
        $Data
    )
    Begin{}
    Process{Write-Output $Data}
    End{}
}

Get-Help Get-Something -Online

By doing this, Google.com will open up in your web browser. Now lets add the .Link and a different URI and watch what happens.

Function Get-Something {
    <#
        .LINK
            http://Learn-PowerShell.net
    #>
    [CmdletBinding(
        HelpURI='http://google.com'
    )]
    Param (
        [parameter(ValueFromPipeLine=$True)]
        $Data
    )
    Begin{}
    Process{Write-Output $Data}
    End{}
}

Get-Help Get-Something -Online

Now when you use the –Online switch, it opens up to my blog homepage instead of Google.com. Something good to know if you are specifying a help URI in both of these places and to know which one takes precedence over the other.

SupportsPaging

This new argument with cmdletbinding allows you to add the following parameters to manage the output of data or to display a total count:

  • -IncludeTotalCount
  • -Skip
  • -First

Each of these will not just work on its own just because you set the SupportsPaging to $True. Some extra work is required on your part to make it work as needed.

IncludeTotalCount

We can first test for the use of –IncludeTotalCount with $PSCmdlet.PagingParameters.IncludeTotalCount and then determine the type of accuracy that will be used for the TotalCount:

  1. 1.0
    1. Some data sources might have the exact number of results retrieved and in this case would have accuracy 1.0
  2. 0.0 – 1.0
    1. Some data sources might only have an estimate and in this case would use accuracy between 0.0 and 1.0
  3. 0.0
    1. Other data sources might not know how many items there are in total and in this case would use accuracy 0.0

Knowing that we now need to apply some data to the $PSCmdlet.PagingParameters.NewTotalCount(<totalcount>,<accuracy>) which will then print out the total count.

Let’s see this in action:

Function Get-Something {
    <#
        .LINK
            http://Learn-PowerShell.net
    #>
    [CmdletBinding(
        SupportsPaging = $True
    )]
    Param (
        [parameter(Position=0)]
        $Data
    )
    Begin{}
    Process{

    }
    End{
        If ($PSCmdlet.PagingParameters.IncludeTotalCount){
            [double]$Accuracy = 1.0
            $PSCmdlet.PagingParameters.NewTotalCount($Data.count, $Accuracy)
        }
    }
}

Get-Something -Data (Get-ChildItem C:\Windows\System32) -IncludeTotalCount

image

Skip and First

For –Skip and –First, there is (yes, you guessed it!) more work that is required to actually make this work like we want it to.

When you specify –Skip, you set the value of $PSCmdlet.PagingParameters.Skip to the specified value. The same goes with –first and $PSCmdlet.PagingParameters.First.

After that there is math involved to make sure that we get the accurate numbers for the first set of data all of the way to the last set of data. I make use of the [Math]::Min() method to make sure that we only get the data that we need and to not go over, especially in the case of the .First which is set to 18446744073709551615 by default. I also have to make sure that if the –Skip goes beyond the total count of items, that nothing is returned by the command.

The function I will be using is here:

Function Get-Something {
    <#
        .LINK
            http://Learn-PowerShell.net
    #>
    [CmdletBinding(
        SupportsPaging = $True
    )]
    Param (
        $Data
    )
    Begin{}
    Process{

    }
    End {
        If($Data.count -gt 0) {
            If($PSCmdlet.PagingParameters.Skip -ge $Data.count) {
                Write-Verbose "No results satisfy the Skip parameters"
            } Elseif($PSCmdlet.PagingParameters.First -eq 0) {
                Write-Verbose "No results satisfy the First parameters"
            } Else {
            $First = $PSCmdlet.PagingParameters.Skip
            Write-Verbose ("First: {0}" -f $First)
            $Last = $First + 
                [Math]::Min($PSCmdlet.PagingParameters.First, $Data.Count - $PSCmdlet.PagingParameters.Skip) - 1    
            }
            If ($Last -le 0) {
                $Data = $Null
            } Else {
                $Data = $Data[$First..$last]
                Write-Output $Data            
            }
            Write-Verbose ("Last: {0}" -f $Last)
        }
        If ($PSCmdlet.PagingParameters.IncludeTotalCount){
            [double]$Accuracy = 1.0
            $PSCmdlet.PagingParameters.NewTotalCount($Data.count, $Accuracy)
        }
    }
}

Lets start by finding the first 10 items in C:\Windows\System32:

Get-Something -Data (Get-ChildItem C:\Windows\System32) -First 10

image

We already know from earlier that there are 2783 items under this folder, so lets skip all of the way to the last 10 by skipping 2773 items.

Get-Something -Data (Get-ChildItem C:\Windows\System32) -Skip 2773

image

We can actually combine all 3 to really narrow down the data:

Get-Something -Data (Get-ChildItem C:\Windows\System32) `
-First 15 -Skip 50 -IncludeTotalCount

 

image

Pretty cool stuff, isn’t it? We are able to combine all three to look at the first 15 items after skipping the initial 50 and then see the total count of items as validation of actually looking for the first 15.

PositionalBinding

This argument of cmdletbinding is actually set to $True by default meaning you don’t have to use it if you are planning on allowing positional parameters. If for some reason, you do not want to allow positional parameters, then you can set it to $False. Also note that this setting can be overwritten if you specify parameter positions using the [parameter(Position=n)] argument.

First lets see it by default:

Function Get-Something {
    <#
        .LINK
            http://Learn-PowerShell.net
    #>
    [CmdletBinding( )]
    Param (
        [parameter()]
        $Data,
        [parameter()]
        $Data1,
        [parameter()]
        $Data2,
        [parameter()]
        $Data3
    )
    Begin{}
    Process{
        Write-Output ("Data: {0}" -f $Data)
        Write-Output ("Data1: {0}" -f $Data1)
        Write-Output ("Data2: {0}" -f $Data2)
        Write-Output ("Data3: {0}" -f $Data3)
    }
    End{}
}

Get-Something 1 2 3 4

image

As you can see, it works like a champ and processes each parameter without having to name it. But now lets set the PositionalBinding to $False and watch what happens:

Function Get-Something {
    <#
        .LINK
            http://Learn-PowerShell.net
    #>
    [CmdletBinding(
        PositionalBinding = $False
    )]
    Param (
        [parameter()]
        $Data,
        [parameter()]
        $Data1,
        [parameter()]
        $Data2,
        [parameter()]
        $Data3
    )
    Begin{}
    Process{
        Write-Output ("Data: {0}" -f $Data)
        Write-Output ("Data1: {0}" -f $Data1)
        Write-Output ("Data2: {0}" -f $Data2)
        Write-Output ("Data3: {0}" -f $Data3)
    }
    End{}
}

Get-Something 1 2 3 4

image

Here you can see that we can no longer use positional parameters with the function, only ‘Named parameters’ can be used with the positionalbinding disabled. As I mentioned earlier, even if you have the argument set to $False, you can still have positional binding if you set each parameter argument to have a position as seen below:

Function Get-Something {
    <#
        .LINK
            http://Learn-PowerShell.net
    #>
    [CmdletBinding(
        PositionalBinding = $False
    )]
    Param (
        [parameter(Position=0)]
        $Data,
        [parameter(Position=1)]
        $Data1,
        [parameter(Position=2)]
        $Data2,
        [parameter(Position=3)]
        $Data3
    )
    Begin{}
    Process{
        Write-Output ("Data: {0}" -f $Data)
        Write-Output ("Data1: {0}" -f $Data1)
        Write-Output ("Data2: {0}" -f $Data2)
        Write-Output ("Data3: {0}" -f $Data3)
    }
    End{}
}

Get-Something 1 2 3 4

image

In Conclusion

So with that, we have now looked at the new features in the CmdletBinding() attribute that is with PowerShell V3 and shown how they can be put to use. While the HelpURI and the PositionalBinding can be over-ruled in other areas, it is still nice to see that they can be configured anyways in a different location.

Please remember that with this being a Beta still, things can and possibly will change by the time the final product is release later this year. But by all means hop onto the V3 train and try out all of the cool new things that come along with it.

Enjoy!

Posted in powershell, scripts, V3 | Tagged , , , , , | 1 Comment