clarification on $psitem or $_

I have kind of a dumb question regarding $psitem. Why is it that even when the pipeline output is a single object, I have to use a for loop to get $psitem working. for example

Get-CimInstance Win32_OperatingSystem |% {$.LocalDateTime-$.LastBootUpTime}

works, but

Get-CimInstance Win32_OperatingSystem | {$.LocalDateTime-$.LastBootUpTime}

gives an error

“Expressions are only allowed as the first element of a pipeline.”

 

Not a dumb question, you are learning. {} is script block when you call it like that, which you cannot pipe to. To create values based on arithmetic, you should use a calculated expression:

Get-CimInstance Win32_OperatingSystem | Select-Object -Property *, @{Name='MyValue';Expression={$_.LocalDateTime-$_.LastBootUpTime}}

While that gets you a value, normally you ware looking for minutes or days, so you would use New-TimeSpan to get the days and round the value:

Get-CimInstance Win32_OperatingSystem | Select-Object -Property *, @{Name='MyValue';Expression={[math]::Round((New-TimeSpan -Start $_.LastBootUpTime -End $_.LocalDateTime).TotalDays,2)}}

Also, $_ and $PSItem are tokens in the context of a row or line. Powershell uses PSObjects, which from a simple explanation is an array of hashtable. Now it’s more complex than that, but for a simple visual demonstration:

#Empty array
$newObject = @()

#Add a hashtable to the array
$newObject += [PSCustomObject]@{
    FirstName = 'John'
    LastName  = 'Smith'
}

#Add a hashtable to the array
$newObject += [PSCustomObject]@{
    FirstName = 'Sally'
    LastName  = 'Wallace'
}

$newObject

Even if you processing a single item like you are getting from WMI in this case, it still looks like this:

@( #array
    @{#hash
        FirstName = 'Sally'
        LastName  = 'Wallace'
    }
)

There is still an array to process, so you still need to process foreach item in the array. The calculated expression $_ is still referencing the context of the row. This is trying to keep the explanation as simple as possible, but hopefully this explains the structure and why you need the foreach.