PowerShell power lesson: This is no ordinary variable

If you follow my work on this site, then you know that I write a whole lot of PowerShell-related content. One of the expressions that I tend to use in my PowerShell scripts on a regular basis is dollar sign underscore dot, (or $_.). Recently, I had someone ask me what those characters meant, and I realized that I have never really taken the time to explain the significance of this series of characters, or why they are used so frequently. So I want to take the opportunity to do that now.

The interesting thing about $_. is that the first of these three characters is a dollar sign. In PowerShell, placing a dollar sign in front of a word (or a series of characters) denotes that the word is a variable. If for example, I wanted to assign my name to a variable, I might use a command like this:

$Name = ‘Brien’

Similarly, PowerShell allows you to map an entire command string to a variable. I might, for instance, create a variable called $Processes and set it equal to the Get-Process cmdlet, as shown below:

$Processes = Get-Process

Why $_. is no ordinary variable

The point is that the dollar sign in $_. indicates that $_. is a variable. However, this is no ordinary variable. Instead, it is often referred to as a variable in the pipeline.

In PowerShell, the word pipeline generally refers to a series of commands that have been joined together. Individual commands are separated from one another by using the pipe symbol (hence the name pipeline). When commands are joined together in a pipeline, the output from one command is used as input for the next command in the sequence.

To give you a more concrete example, consider the Get-Process cmdlet. This cmdlet retrieves a list of the processes that are running on the machine. You can then use the pipe symbol to treat the list of processes as the input for the next command in the series. You might, for example, want to narrow down the list to look at some specific processes. To do so, you could pipe the Get-Process command’s output into the Where-Object cmdlet, which can filter that output. Here is an example.

Get-Process | Where-Object {$_.ProcessName -eq ‘dllhost’}

This command shows all of the processes whose name is equal to dllhost. You can see the command and its output shown in the image below.


You will notice that the command that I am using here includes the variable in the pipeline. Before I begin dissecting the command and explaining how it and the variable in the pipeline work, there is one thing that I want to clarify. Although $_. Is commonly referred to as a pipeline variable, there is nothing stopping you from using a normal variable in a pipeline. PowerShell is extremely flexible when it comes to the ways in which it allows variables to be used.

If you look back at the previous screen capture, you will notice that the information that is presented within the output is divided into a series of columns and that each of those columns has a name. The Where-Object portion of the command is looking at a specific column (ProcessName) and is checking to see if it is equal (-EQ) to a particular value (dllhost). So where does the pipeline variable come into play?

The pipeline variable essentially acts as a shortcut. Earlier I mentioned that it is possible to assign a command to a variable. If you look at the image below, you can see that I have mapped the Get-Process command to a variable called $Processes. If I were to simply type the variable name, PowerShell would generate the same output as if I manually executed the Get-Process cmdlet, which you can also see in the figure.

Now, if I were to type the variable name, followed by a period and a column name, PowerShell would output the contents of that column. You can see what this looks like in the next image.

So with that in mind, consider our original command:

Get-Process | Where-Object {$_.ProcessName -eq ‘dllhost’}

In this command, our pipeline variable also uses a period and a column name. In fact, the underscore character is essentially just taking the place of a variable name, thereby saving us the trouble of declaring a variable.

So if that is true, then we should be able to observe similar functionality from the variable that we have already declared ($Processes). Since we set the $Processes variable to be equal to the Get-Process cmdlet, we can replace the Get-Process portion of the command above with the variable name. The commands would, therefore, look like this:

$Processes | Where-Object {$_.ProcessName -eq ‘dllhost’}

You can see the output below.

So in this command the $_. variable acts as a shortcut. We reference the $Processes variable once and then use $_. throughout the rest of the command rather than having to type out the full variable name. But just to show you that the pipeline variable truly is just a shortcut for referencing the variable by name, check out the screen capture below. Here, I have gotten an output that is very similar to that of my original command by referencing $Processes.ProcessName.

Take the shortcut

As you can see, $_. is essentially just a shortcut that prevents you from having to retype a variable name within a pipeline command. Even so, it is worth noting that a pipeline variable and a full expression of the variable are not interchangeable. If you were to use:

$Processes | Where-Object {$Processes.ProcessName -eq ‘dllhost’}

Instead of:

$Processes | Where-Object {$_.ProcessName -eq ‘dllhost’}

The output would not be filtered as requested.

About The Author

18 thoughts on “PowerShell power lesson: This is no ordinary variable”

    1. Unfortunately, I don’t really have a good answer for you. I don’t know enough about C# to make an intelligent comparison.

    2. Kindof.

      The C# Equivalent of

      $Processes | Where-Object {$_.ProcessName -eq ‘dllhost’}

      would be:

      Processes.Where(p => p.ProcessName is “dllhost”)

      So C#’s `p` is the same as powershell’s `$_`.

  1. $_ is not a replacement for $processes in your example. This part of your explanation is incorrect.

    What actually happens, using your example, is the $_.ProcessName uses the results of the previous $processes command, while $Processes.ProcessName re-runs the $Processes command and uses the new results.

    This isn’t a big deal in this specific case, but in many cases it will lead to serious problems if for some reason you feel the need to replace $_ with the original variable name (e.g. for clarity). It will not always work as you expect.

    You can test this by wrapping both of them in Measure-Command {}, it shows they are actually doing very different things.

    $_.ProcessName is roughly 15 times faster than $Processes.ProcessName. Here are my results (run 10 times each to exaggerate the differences):

    Measure-Command { 1..10 | foreach {$processes | where {$processes.processname -like ‘powershell’}}}

    Days : 0
    Hours : 0
    Minutes : 0
    Seconds : 0
    Milliseconds : 731
    Ticks : 7311093
    TotalDays : 8.46191319444444E-06
    TotalHours : 0.000203085916666667
    TotalMinutes : 0.012185155
    TotalSeconds : 0.7311093
    TotalMilliseconds : 731.1093

    Measure-Command { 1..10 | foreach {$processes | where {$_.processname -like ‘powershell’}}}

    Days : 0
    Hours : 0
    Minutes : 0
    Seconds : 0
    Milliseconds : 44
    Ticks : 449571
    TotalDays : 5.20336805555556E-07
    TotalHours : 1.24880833333333E-05
    TotalMinutes : 0.000749285
    TotalSeconds : 0.0449571
    TotalMilliseconds : 44.9571

  2. Brian, Thank you for the clear explanation.
    I’ve been programming in various languages since 1979 (basic, assembler, Fortran, etc. and also in applications like dBase), not as a technical IT professional but rather for enjoyment and to accomplish tasks in my job or at home. So, I’m pretty comfortable with most programming concepts but the $_. Symbol had been confusing me and your explanation cleared it up quite well. It is always a surprise to me how a clear explanation of a term can clear up a major frustration when trying to understand a new programming language.

    I do have one question on clarification about your last paragraph with regard to the fact that the output would not be filtered as requested if the full expression were used instead of the pipeline variable.

    I am assuming that the answer may be explained by Jeff’s comment that the process is rerun instead of using the output that is piped from when it is initially run. Is that correct. If not, could you go into more of an explanation about your last paragraph?

  3. Brein, this was epic. I’m new to Powershell, been seeing so many examples using $_ but couldn’t get a explanation I understood. Your article tied up so many loose ends. Thanks a bunch!

  4. Good article. Might be worth pointing out that what is being sent through the pipeline is an array of objects. The $_ pipeline variable allows you to access the members of those objects, meaning its properties, methods and events.

    So you can see what members are available to you within the pipeline by piping the first object to Get-Member like:

    `$Processes[0] | Get-Member`

    I’ve been scripting with Powershell since its pre-release Monad days, and I can tell you that when I was starting out, Get-Member was by far the Cmdlet I used most. Its value cannot be understated imho.

    The $_ special variable is also used in other ways too. For example in parameter validation in advanced functions:

    function Test-Parameter
    [ValidateScript({ Test-Path $_ -PathType ‘leaf’ })]
    [string] $Path


    So in that case, $_ refers to the variable $Path when the function is called. It’s also used in a couple other ways in advanced functions. Great article though

Leave a Comment

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Scroll to Top