Trace Your Commands by Using Trace-Command


Summary: Boe Prox uses Trace-Command to understand commands.

Honorary Scripting Guy and Windows PowerShell MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy. This is the fourth part in a series of five posts about troubleshooting PowerShell scripts/functions. The series includes:

  1. Provide Support by Using Verbose and Debug Streams
  2. Troubleshoot by Using Set-PSDebug
  3. Enforce Better Script Practices by Using Set-StrictMode
  4. Trace Your Commands by Using Trace-Command (this post)
  5. Use the PowerShell Debugger

Today I am talking about using Trace-Command to look at your commands and to help better understand how they are working behind the scenes.

Have you ever noticed that you can type Get without the actual verb and get cmdlets? And what is really happening when you pipe objects into a cmdlet and the cmdlet magically finds the right parameter value (well, sometimes)? I will show you this and some other things during the course of this post by using Trace-Command and a number of supporting parameters.

Discovering commands

When you type a command in Windows PowerShell, it first goes through a command discovery that attempts to locate the command that you have typed. If found, Windows PowerShell attempts to run the command. For instance, using Ping initiates a command discovery to locate Ping.exe prior to running the command. To see this, I am going to use Trace-Command and supply a few parameters:

 Name

 Determines which Windows PowerShell components are traced.

 Expression

 A script block with the command that will be traced.

 PSHost

 Outputs the trace messages to the Windows PowerShell console.

Knowing this, I will construct my trace command to look at how ping.exe is discovered:

Trace-Command –Name CommandDiscovery –Expression {ping localhost} -PSHost

Image of command output

In this image, you can see that a scan is done for Ping with various extensions against multiple paths until ping.exe is found. Then it is run using the parameters provided.

I mentioned previously that the Get cmdlets can run without specifying the actual verb, so let's use this same approach to see what happens:

Trace-Command –Name CommandDiscovery –Expression {service} -PSHost

Image of command output

I’ll save you from the rather large amount of trace output and get to the very end of it. First all, of the items that we looked for in the previous command (which means various extensions and paths) are traversed. At the end (when nothing has been found), Windows PowerShell appends Get- to the command—and just like that, we have Get-Service, which then runs.

What am I piping to another cmdlet?

When piping output from one cmdlet to another (or piping some sort of data to another cmdlet), we know that Windows PowerShell will work its magic. It will process that input to the cmdlet and then produce something—whether it is output or some other kind of change.

To better see what is happening behind the scenes, we can look at ParameterBinding, ParameterBinderBase, ParameterBinderController, and TypeConversion:

$Names = @(

'ParameterBinderBase',

'ParameterBinderController',

'ParameterBinding',

'TypeConversion'

)

Trace-Command -Name $Names -Expression {'10/10/2014' | Get-Date} -PSHost

Image of command output

There is a lot of stuff happening here with all of the trace information. We can see the ParameterSetName for the Get-Date cmdlet, and then it begins to check mandatory parameters. From there, the process begins working through a set of checks to try to map the input to the parameter that supports the pipeline. In this case, it is the –Date parameter, which is looking for a [datetime] object.

The following list shows the flow of checks for data coming through the pipeline (named parameters are always binded first).

Order of parameter binding process from pipeline:

  1. Bind parameter by Value with same Type (no coercion)
  2. Bind parameter by PropertyName with same Type (no coercion)
  3. Bind parameter by Value with type conversion (coercion)
  4. Bind parameter by PropertyName with type conversion (coercion)

Knowing how this works can help ensure that you are properly sending the right data to another command and that the proper parameter is being used in the binding of the data.

Now let’s take a look at an issue that I have seen people having: taking files and folders from Get-ChildItem and attempting to move the data without specifying a parameter name.

PS C:\> Get-ChildItem | Select -first 1 | Move-Item C:\OneDriveTemp –WhatIf

Move-Item : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.

At line:1 char:35

+ Get-ChildItem | Select -first 1 | Move-Item C:\OneDriveTemp -WhatIf

+                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidArgument: (PoshAsyncJob:PSObject) [Move-Item], ParameterBindingException

    + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.MoveItemCommand

So what happened here? Wouldn’t the input be passed through and then move to the specified location? Time to check out the trace of this command and find out!

Trace-Command -Name ParameterBinding -Expression {Get-ChildItem | Select -first 1 | Move-Item C:\OneDriveTemp -WhatIf} -PSHost

Image of command output

This is where positional parameters come into play. The path specified is binding first to the first parameter in the list (in this case, –Path). That means that the incoming data from Get-ChildItem now has to find a parameter to bind to because Path has been taken, and it ends up failing. We need to specify the –Destination parameter for the destination path for this to properly work:

PS C:\> Get-ChildItem | Select -first 1 | Move-Item -Destination C:\OneDriveTemp –WhatIf

What if: Performing the operation "Move Directory" on target "Item: C:\Users\proxb\Desktop\PoshAsyncJob Destination: C:\OneDriveTemp\PoshAsyncJob".

It works like a champ now!

Tomorrow, I will wrap-up this week of using built-in Windows PowerShell commands to help troubleshoot scripts by working with the Windows PowerShell debugger in the console and the ISE.

We invite you to follow the Scripting Guy on Twitter and Facebook. If you have any questions, send email to the Scripting Guy at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, see ya!

Boe Prox, Windows PowerShell MVP and Honorary Scripting Guy

Comments (2)

  1. Chen V says:

    Thanks. Good explanation.

  2. ron says:

    Prepends, not appends, GET. 🙂

Skip to main content