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.