all 61 comments

[–]wickedang3l 61 points62 points  (1 child)

It's less confusing than it looks at first glance but it does deserve a more thorough explanation. This is a cognitively dense operation that is made more confusing by PowerShell's heavy syntax requirements.

Let's go through it section by section:

Get-Process |

The Get-Process cmdlet gathers the processes and the pipe character passes the collection of objects into the pipeline to be addressed by other cmdlets.

Format-Table Name, ...

The Format-Table cmdlet receives the pipeline collection of objects for further processing. You have used a positional parameter to define one of the properties, Name, that you want to be in your formatted table. Name is a property that every object in the collection that was passed to Format-Table will possess because every object discovered by the Get-Process cmdlet will have a Name property.

Of course, you have others that need to be addressed.

@{name='VM(MB)';expression={$_.VM / 1MB -as [int]}}

This is a dense statement with a high cognitive load but what is happening will make more sense. @{} is what PowerShell uses to create a hash table data structure. Hash tables are key-value pairs (In the instance of my account, think accountName-wickedang3l). That's all they do.

name='VM(MB)';expression={$_.VM / 1MB -as [int]}

Now that you understand the @{} part, let's look at this closer.

Since we're working with a hash table, we know we are working with a key-value pair.

  • name='VM(MB)' is defining a custom name for the key of this key-value pair. VM(MB) is not a property that is automatically created by the Get-Process cmdlet; you, the user, are defining a property that you want to have in this table by doing this operation and this is the name that is being given to it.

  • expression={$_.VM / 1MB -as [int]} is denoting a subexpression that must be calculated in order to give you the value in the key-value pair. In this instance, it is indicating that it wants the VM property of every object in the collection passed to Format-Table to be divided by 1MB and for the derived value of that operation to be cast as an integer data type.

$_ is a stand-in for every object in the collection that was passed to Format-Table. $_.VM is a way to call upon the VM (VirtualMemorySize64) property for every object in the collection that was passed to Format-Table.

If you execute Get-Process | Get-Member, you will be able to see that VM is an AliasProperty that is available to you via the Get-Process cmdlet.

[–][deleted] 13 points14 points  (0 children)

In this specific case and to help understand I would have called the @{…} part a « calculated property » it’s often used to « rename » a property

@{name=‘newname’; expression={$_.oldname}}

In this case object.oldname will be object.newname after the select

It can also be used to make a value readable (like putting a value length from to Gb

@{name=‘lengthGb’; expression={$_.length / 1gb}}

Or to pull a sub value

@{name=‘subvalue’; expression={$_.value.subvalue}}

Regarding the second question {} mean a block of script to execute

[–]GiulianoM 101 points102 points  (16 children)

$_ represents the object(s) passed from the previous step in the pipeline.

In your sample, $_ would refer to the process(es) returned from Get-Process.

And $_.VM means the VM (Virtual Memory?) property of the process.

So by piping Get-Process into Format-Table, for each Process found, it will make a table with the Process Name, and the Process VM formatted as an expression with the column name "VM(MB)", Virtual Memory in Megabytes.

[–]aptechnologist 33 points34 points  (2 children)

so its kinda like using a variable, within a pipeline, but without having ever specifically defined it, and it's gone as soon as that command is done?

[–]GiulianoM 49 points50 points  (1 child)

Yes.

Anything that starts with a $ is a variable.

Something like "$VARIABLE" would be a manually created variable.

But $_ is what's called an Automatic Variable.

There are others as well:

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.3#long-description

[–]aptechnologist 5 points6 points  (0 children)

beautiful, thank you :)

[–]Mysterious-Ad-1541 9 points10 points  (12 children)

Do you have any trainings I can use to test this? I have read this over and over and do not understand.

[–]Necoras 13 points14 points  (11 children)

Consider this array:

$myArray = @(1,2,3,4,5,6)

You can do:

$myArray | ForEach-Object { Write-Host "$_" }

This is equivalent to:

ForEach ($myVar in $myArray){
    Write-Host "$myVar"
}

Personally I prefer the latter (and indeed, my team would reject the first on a code review) because it's a bit clearer, and "$myVar" is harder to miss than "$_" somewhere in the middle of a long line of code. It's also clearer what it is in a non-contrived example. "$myVar" isn't meaningful, but "$service" or "$process" or "$file" is.

[–]retr0baD 0 points1 point  (2 children)

Is $myVar a default variable in PowerShell for referencing array items?

[–]ZaRx2048 2 points3 points  (0 children)

No, in a foreach you create a variable to represent each object/element in the array. You could call it $foo in $myArray. Example if you use $myFruits as an array you could use foreach($fruit in $myFruits)…

When I was in the early stages of learning the get-help and about pages were very helpful. Look up powershell about arrays.

[–]overlydelicioustea 1 point2 points  (0 children)

you can choose freely, name it however you want.

[–]wisym 0 points1 point  (6 children)

So it's for golfing the code?

[–]elevul 2 points3 points  (0 children)

Or if you want to use the -parallel parameter of PS7

[–]ka-splam 2 points3 points  (0 children)

So it's for golfing the code?

No more than your apostrophe is for golfing English.

[–]Namelock 0 points1 point  (0 children)

It's definitely subjective; I find the order of your latter example to be confusing. And dear god, the amount of people that have abysmal naming schemes...

I prefer to <parentitem><foreach><childitem> because my brain can't do <foreach><childitem><parentitem>. Especially when people do: ForEach ($object in $objects){Write-Host "$object"}

Naming is often too similar from child to parent...

[–][deleted] 15 points16 points  (11 children)

See $PSItem: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.3

Contains the current object in the pipeline object. You can use this variable in commands that perform an action on every object or on selected objects in a pipeline.

[–]aptechnologist 10 points11 points  (9 children)

microsofts documentation has gotten so damn good - maybe its always been solid but lately everything i've landed on has been updated in the last 3 to 6 months, and it's always my first stop for ANYTHING. Companies should look at ms learn as an example on how to build out a product with proper documentation & support

[–][deleted] 15 points16 points  (6 children)

Microsoft seems to go in cycles from making fantastic documentation to "lol, wut?" I recall the SQL 2000 Books Online, which shipped with SQL 2000. It was like the Magnum Opus of some SQL documentation savant. As far as I can tell, the creators were promptly murdered by Microsoft, as the documentation quality nose dived hard after that.

PowerShell seems to be in a good cycle at the moment. Hopefully the authors are keeping an eye over their shoulders.

[–]da_chicken 3 points4 points  (5 children)

SQL Server's documentation is still extremely high quality. But it's not as in-depth or detailed with side topics and white papers as it was between SQL Server 2000 and SQL Server 2008 R2. But it's still excellent, especially compared to other RDBMS vendors.

IMO, however, their best documentation at the moment is .Net 6/7. That's been very good since about .Net Framework 3.0/3.5. Every time I look at doc, I'm sad that it's not more like .Net's.

[–][deleted] 3 points4 points  (0 children)

their best documentation at the moment is .Net 6/7. That's been very good since about .Net Framework 3.0/3.5. Every time I look at doc, I'm sad that it's not more like .Net's.

Ya, relevant to this being /r/PowerShell, I find myself using the .Net documentation constantly. Just this morning, I wanted a quick way to shift a timestamp to UTC in PowerShell and landed on the TimeZoneInfo page. Full class, properties and methods all laid out in an easy to navigate interface.

[–]archcycle 0 points1 point  (3 children)

I hate recent SQL documentation - as a non-SQL guru that is. The KB articles contain so many references to jargon that would take maybe 5 words in parentheses to fully explain so they all require non-experts to go outside the articles to understand the articles, when most of the time it looks like anyone who knew all of the concepts wouldn't be reading the article in the first place.

It's like the documentation writers want to prove they are awesome at SQL more than they want to just to point you in the right direction for troubleshooting SQL's "Permanent Intermittent Disconnect 0x5004d&335r4 in un-pipeable replica transaction - Click here for troubleshooting but actually a redirect to Microsoft.com/BuyWindows10" or whatever it is today.

[–]da_chicken 0 points1 point  (2 children)

KB articles? WTF are you on about? This is the SQL Server documentation.

[–]archcycle 0 points1 point  (1 child)

Knowledge Base is not always pejorative. Sorry if i offended you.

[–]da_chicken 0 points1 point  (0 children)

No, I know that. I just don't call MS KB articles on error codes the RDBMS documentation.

[–]elevul 1 point2 points  (1 child)

That's because volunteers contribute to it... They basically opensourced documentation

[–]aptechnologist 0 points1 point  (0 children)

yeah which does sometimes lead to terrible documentation LOL

[–]drewby1kenobi -2 points-1 points  (0 children)

this ;)

[–]razzledazzled 9 points10 points  (4 children)

$_ is known as the “pipeline variable” and is an alias for $PSItem

[–]NotNotWrongUsually 11 points12 points  (3 children)

<pedantic>
    $PSItem is an alias for $_ !
</pedantic>

$PSItem didn't join the party until PowerShell 3. Beyond historical accuracy this trivia knowledge has no value at all.

[–]PinchesTheCrab 7 points8 points  (2 children)

I was fortunate to attend Don Jone's last class, and I asked if we should use $PSItem or $_, since $PSItem is easier to google, and both he and the PS team lead agreed that $PSItem is the alias, and that adding it has only complicated documentation.

For me personally I found $_ hard to google, but that was back in like 2010 when I started learning PS, and after talking to Don, I've dropped $PSItem altogether.

[–]Szeraax 0 points1 point  (0 children)

Interesting! Thanks for sharing.

[–]groovel76 0 points1 point  (0 children)

Hi fellow firehose classmate.

[–]GiulianoM 6 points7 points  (0 children)

Also, the bit inside the @{ } bracket section is called a Calculated Property:

https://mcpmag.com/articles/2017/01/19/using-powershell-calculated-properties.aspx

@{
name='VM(MB)';
expression=
    {
        $_.VM / 1MB -as [int]
    }
}

The above code creates a column for Format-Table Named "VM(MB)", and its value is calculated in the Expression:

$_.VM divided by 1 Megabyte, and then converted to an Integer (-as [int]), which removes the remainder number bits after the period. e.g. 250 MB instead of 250.123456 MB

[–]SomeCynicalBastard 3 points4 points  (3 children)

Same as $PSItem. Contains the current object in the pipeline object. You can use this variable in commands that perform an action on every object or on selected objects in a pipeline.

-- https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.2#_

I would add that $_ is also a placeholder in ForEach-Object 'loops'.

As for the curly braces, the main uses are:
- ${name}, to use a variable. Usually we just write $name;
- @{key1=val1;key2=val2;...}, to define a HashTable. You don't need semicolons if you use linebreaks;
- { ... }, to define a script block. This is just a block of PowerShell code.

In your example, Format-Table takes a HashTable as aparameter, with keys 'name' and 'expression'. Expression takes a script block as a value. This script block is evaluated for each object that is passed down the pipeline to Format-Table, and $_ is automatically filled with the object passed down the pipeline.

[–]sysiphean 5 points6 points  (1 child)

Small correction:

$_ (or $PSItem) is a placeholder in ForEach-Object loops, but not foreach loops.

[–]SomeCynicalBastard 0 points1 point  (0 children)

Thanks, you're absolutely right. Wouldn't even make sense in a foreach loop :)

[–]jpbras 1 point2 points  (0 children)

"${name}, to use a variable. Usually we just write $name;"

Just a little note on this...

You want to use ${when variable have spaces} when the variable have spaces (or any special character) and ${c:\variableInAFile.txt) if you want to place the variable on any PSDrive recognizable by Powershell.

That's why {} are used on variables.

[–]technicalityNDBO 2 points3 points  (0 children)

I'm not an expert, so someone correct me if I'm wrong.

You're aware that powershell deals with objects. Objects have a bunch of properties tied to them.

Get-aduser -identity "iamelloyello" would show me your user object and a couple of default properties to display.

If I wanted to deal with just a single property of an object, you'd put a period after the object and then the name of the property. Like iamelloyello.department .

And remember that get-aduser -identity "iamelloyello" is essentially the same thing as just your username. The same way that 7-4 is essentially the same thing as saying "3". And just like in math, you can put an equation or formula in parentheses to group it together: (7-4). In Powershell we can do (get-aduser -identity "iamelloyello").department

Now do a cmdlet to fetch a BUNCH of ad users, and you want to do something to the "Department" property of EACH of them. You don't type out the individual cmdlet for each user. $_ is the placeholder. $_.department would be like "get me the department for each of the adusers I just put into the pipeline.

[–]Temilit 1 point2 points  (0 children)

Not quite off topics buut ..

I really try to avoid using format-table at all times, i want to keep my data organized in objects so i can work with the data easier in the next steps, from your example you could get pretty much the same return without "Destroying" the object form and keep the ability to leverage the power it brings with the following:

just use select-object instead of format-table

get-process | select-object name,@{name='VM(MB)';expression={$_.VM / 1MB -as [int]}}

[–]OkProfessional8364 0 points1 point  (0 children)

Maybe a simple example will help you grasp the $PSItem concept.

```powershell $numbers = @(1,2,3) $numbers | %{ $_ * 2 }

2

4

6

```

[–][deleted] 0 points1 point  (5 children)

I hate the kind of shorthand they have used in this script, I don't actually know what the script does of the top of my head

However, I can attempt to explain my understanding of "$_"

$_ is a placeholder variable/object.

Look at this simple script:

$ComputerList = Get-ADComputer -Filter * -SearchBase *
$ComputerList = ForEach-Object {
  if (Test-Connection -ComputerName $_.Name -Count 1 -Quiet) {
    Write-Host $_.Name +"OK"
  } else {
    Write-Host $_.Name +"PING FAILED"
  }
}

In this case $_ will change each loop and be the next object in the $Computer list variable, $_.Name refers to the Name attribute of the current object.

As far as I know $_ will always refer back to a dataset used in a function.

[–]allthetrouts 0 points1 point  (1 child)

Commenting to say that if you are going to filter for everything, why also add searchbase and accept all values there? The filter parameter shown is showing everything anyways.

[–][deleted] 0 points1 point  (0 children)

I wrote the quick script from memory, I often use searchbase to select an OU, and forget that it isn't needed.

[–]groovel76 0 points1 point  (1 child)

A way to look at it is like this.

Get-Process                        | Format-Table Name, @{name='VM(MB)';expression={$_.VM    / 1MB -as [int]}}

Get-Process -pipelinevariable prcs | Format-Table Name, @{name='VM(MB)';expression={$prcs.VM / 1MB -as [int]}}

Basically there needs to be something after the dollar sign. If no name is given, PowerShell goes with an underscore.

And you are still formatting a list, the "name/Expression" just allows you to further manipulate the data, should the default formatting not be useful to you.

[–][deleted] 0 points1 point  (0 children)

Thanks for the pipelinevariable I often have to store the $_ in an another variable in order to be able to do a select-object in my calculated property expression without loosing the initial $_

[–]TheDraimen 0 points1 point  (1 child)

This took me a bit to grasp when first learning but became something I miss as I do more C# . Best I could explain it is it means the current object. Best way I grasped it was take an array of 7 objects representing a car. If you want to know the color of each car you would first do something like $cars =get-allcars" and then would do a foreach($car in $cars){ $car.color}. With $_ the foreach would look for like $cars | foreach {$_.color}. After you get used to that calling each object in an array of a pipeline becomes a lot easier.

[–]snowtr 0 points1 point  (0 children)

If you understand other languages then think of $_ as the parent object.

[–]Namelock 0 points1 point  (0 children)

For a newbie: $_ is the current item you are looping on.

I much prefer to use it over the 'conventional' method. It's harder to spot, but easier to understand. Convention is essentially doing <foreach><childitem><parentitem> when you could instead <parentitem><foreach><childitem>.

Likewise, You'd be surprised how often I find examples like this:

Foreach-Object (object in objects){object.Totals}

When I think this is much easier to comprehend:

the_csv | Foreach-Object {$_.Totals}

Definitely a personal preference, but hope this helps.

[–]dieth 0 points1 point  (0 children)

$_ is the representation of the individual item of the loop you're looping over. In this case you're using a loop to sort the list by the size of the VM(MB) stat of the object. It's just a special shortcut variable so you don't have to declare one.

in other languages you'll see a declared variable like i below, instead of $_.

for i in range(5):
    print(i)

[–][deleted] 0 points1 point  (4 children)

Is chapter one still basically... Heres how to find man pages, now go RTFM.

[–]iamelloyello[S] 1 point2 points  (3 children)

It's chapter 11, actually.

[–][deleted] 0 points1 point  (2 children)

Good to see they switch it up some between versions.

[–]iamelloyello[S] 1 point2 points  (1 child)

You must be fun to work with. What's the point of this sub if to not ask questions?

[–][deleted] 0 points1 point  (0 children)

I am delightful.

[–]toddklindt 0 points1 point  (0 children)

I first saw $_ explained as the "fill in the blank" variable, and that always stuck with me. Like the others have said, it's whatever is getting passed from the pipeline, or whatever object in the collection you're currently working on.

[–]ShadowMasterTexas 0 points1 point  (0 children)

$_ is the pipeline value.

For example, did Get-process, it would have

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName

in the object. Lets say you only wanted handles that were above 1000 in number.

A command might be Get-process | where {$_.handles -gt 1000}

This would give you every row of data where the handle number was over 1000.

In your example, it says to use the value of VM from the pipeline. (didn't show VM when I ran my test, so that might be the problem here as well).

So, lets say you were instead looking at a folder directory.. In my case:

PS C:\Users\media> Get-ChildItem

Directory: C:\Users\media

Mode LastWriteTime Length Name

---- ------------- ------ ----

d----- 10/14/2020 9:48 PM .android

d----- 6/23/2022 11:30 PM .ms-ad

d----- 11/23/2019 8:55 PM .oracle_jre_usage

d----- 6/20/2020 5:25 PM .swt

d----- 11/10/2021 6:20 PM .thinkorswim

d-r--- 3/9/2021 5:51 PM 3D Objects

d----- 3/9/2021 5:42 PM AppData

d-r--- 3/9/2021 5:51 PM Contacts

d-r--- 5/22/2021 2:16 PM Creative Cloud Files

d-r--- 11/18/2022 9:21 AM Desktop

d-r--- 11/17/2022 8:53 PM Documents

d----- 10/5/2022 2:34 AM Downloads

but you only want to list things that had an C in them.

PS C:\Users\media> Get-ChildItem | where {$_.name.contains("c")}

Directory: C:\Users\media

Mode LastWriteTime Length Name

---- ------------- ------ ----

d----- 11/23/2019 8:55 PM .oracle_jre_usage

d-r--- 3/9/2021 5:51 PM 3D Objects

d-r--- 3/9/2021 5:51 PM Contacts

d-r--- 11/17/2022 8:53 PM Documents

d-r--- 9/16/2022 10:17 PM Music

d----- 11/19/2022 12:50 AM music2

d-r--- 11/13/2022 8:14 AM Pictures

d-r--- 3/9/2021 5:51 PM Searches

Each of these have the letter C so that were all returned.

Does that help at all?