Get-Member doesn't show Count member on a (Start-Job) object? - powershell

As far as I know Get-Member can display all the members of an object, but I'm seeing a Count member that I can't explain:
PS> $job = start-job {dir c:\ }
PS> $job | get-member -Force -View All -MemberType All | select-string Count
# outputs nothing
PS> $job.Count
1
Where is the Count member coming from?

This is a synthetic property that was added in V3:
http://blogs.msdn.com/b/powershell/archive/2012/06/14/new-v3-language-features.aspx
You can now use Count or Length on any object, even if it didn’t have
the property. If the object didn’t have a Count or Length property,
it will will return 1 (or 0 for $null). Objects that have Count or
Length properties will continue to work as they always have.
PS> $a = 42
PS> $a.Count
1

Related

How does $_ work when piping in PowerShell?

I'm confused how the $_ variable works in certain contexts of piping. In this example for backing up a Bitlocker key:
Get-BitlockerVolume | % {$_.KeyProtector | ? RecoveryPassword | Backup-BitlockerKeyProtector -MountPoint $_.MountPoint}
This is how I read it in English:
Get all BitLockerVolume objects
For each BitLockerVolume object, pipe the KeyProtector fields forwards
Pipe KeyProtector objects forwards further for those with a RecoverPassword
Run the Backup-BitlockerKeyProtector, and supply the MountPoint
However, MountPoint is a field of the BitLockerVolume object, as shown here:
PS C:\Windows\system32> Get-BitLockerVolume | Get-Member | Where-Object {$_.Name -eq "MountPoint"}
TypeName: Microsoft.BitLocker.Structures.BitLockerVolume
Name MemberType Definition
---- ---------- ----------
MountPoint Property string MountPoint {get;}
So, for the entire block wrapped in brakcets { }, will the $_ variable ALWAYS be the same through any amount of piping? For example, the object we are piping forwards is changing. It's no longer a BitLockerVolume Object, but instead a KeyProtector object. So will the $_ always refer to the BitLockerVolume object in this case, or will it change further down the pipeline depending on different types of objects piped further through the chain?
So $_ is the info from the current pipe.
1,2 | %{
$_
}
response
1
2
while
1,2 | %{
"a","b" | %{
$_
}
}
response
a
b
a
b
We can see in the first that the output from %_ is from the last info given which is 1,2. While the next example still loops 1,2 but the output is from the pipe inside a,b.
There are ways around this by storing the first pipe information into a variable in the second pipe
1,2 | %{
$Num = $_
"a","b" | %{
$Num
}
}
which case the output is
1
1
2
2
In the example you gave lets look at it formated
Get-BitlockerVolume | % {
$_.KeyProtector | ? RecoveryPassword | Backup-BitlockerKeyProtector -MountPoint $_.MountPoint
}
You have 2 different pipes. The First is getting 'BitlockerVolumevolume'.
The second starts with you sending the BitlockerVolume's KeyProtector.
Its like saying
For each Bitlocker volume, Get KeyProtector.
For each KeyProtector, Get me ones that have the member RecoveryPassword
Foreach KeyProtector with member RecoveryPassword, Backup Bitlocker Key Protector Using KeyProtector's Mountpoints
So on one final note I would also assume the example you gave wouldnt work.
What you might be looking for is this...
Get-BitlockerVolume | % {
$MountPoint = $_.MountPoint
$_.KeyProtector | ? RecoveryPassword | Backup-BitlockerKeyProtector -MountPoint $MountPoint -KeyProtectorId $_.KeyProtectorId
}
Let's expand the aliases and fill in the implied parameters. $_ can only be used inside script blocks '{ }' that are options to cmdlets. Just because you're in a pipe, doesn't mean you can use $_ . The $_ here belongs to Foreach-Object. Where-Object is using a comparison statement.
Get-BitlockerVolume | Foreach-Object -Process {
$_.KeyProtector | Where-Object -Property RecoveryPassword |
Backup-BitlockerKeyProtector -MountPoint $_.MountPoint
}
I know there are already good answers here, but I feel one important question was not addressed. The question of what happens to $_ throughout the Foreach-Object {} block when there is nesting. I am going use ArcSet's example since that was the answer selected.
1,2 | % {
"$_ before second foreach"
'a','b' | % {
"$_ inside second foreach"
}
"$_ after second foreach"
}
1 before second foreach
a inside second foreach
b inside second foreach
1 after second foreach
2 before second foreach
a inside second foreach
b inside second foreach
2 after second foreach
Notice that $_ becomes the current object being processed by the code within the Foreach-Object {} blocks. When entering the second Foreach-Object block, $_ changes. When exiting the second Foreach-Object block, $_ changes back to the object that will be continued to be processed by the remainder of the first block. So $_ neither remains the same nor is lost during the block processing. You will need to either assign $_ as another variable or in applicable situations use the -PipelineVariable switch to access those objects within different blocks.
Id' like to build on ArcSet's answer just a little bit. Since I finally understood the value of the $PSItem is changing when the type changes down the pipeline, I ran this code to do a little check.
Get-BitLockerVolume | % {$_.GetType()}
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False BitLockerVolume System.Object
True False BitLockerVolume System.Object
True False BitLockerVolume System.Object
Here we can see some objects returned by the pipeline are of the BitLockerVolume type.
Now, based on my original question/example, if we pipe it further based on KeyProtector the object type will change for the $PSItem variable.
Get-BitLockerVolume | % { $_.KeyProtector | ? RecoveryPassword | % {$_.GetType()}}
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False BitLockerVolumeKeyProtector System.Object
True False BitLockerVolumeKeyProtector System.Object
So at this point, at the end of the pipeline, we execute some other cmdlet like Backup-BitlockerKeyProtector and reference the $PSItem variable, aka $_, then it will refer to the object types last passed through the pipeline, in this case, the objects would be of the BitLockerVolumeKeyProtector type.

Variablize Method Call in Powershell

I'm looking for a way to dynamically call a method or property of a function. For example say I wanted to call every property of $Error[0]. I could get a list of all the property names with:
$a = ($error[0] | get-member -type property | select Name)
I would then like to do something like this:
foreach($b in $a){
$error[0].&($b.Name)
}
But the call operator (&) doesn't resolve $b.Name like I would expect (it should resolve to 'CatagoryInfo'). Is there anyway to do something like this?
You could use different way to retrieve properties:
$Properties=$Error[0].PSObject.Properties
This will return collection of PSPropertyInfo objects. PSPropertyInfo objects have Value property which you could use to get or set property value:
$Properties|ForEach-Object {'Name={0}; Value={1}'-f$_.Name,$_.Value}
Also note of some possible problems in your way of retrieving properties' values. PowerShell allows you to access IDictionary elements with property syntax. Problem here is that PowerShell prefer to access to a collection element rather than actual property:
$Hashtable=#{}
$Hashtable.Count
# 0
$Hashtable.Add('SomeName',10)
$Hashtable.SomeName
# 10
$Hashtable.Count
# 1
$Hashtable.Add('Count',20)
$Hashtable.Count
# 20
$Hashtable|Select-Object -ExpandProperty Count
# 2
You can use Invoke-Expression:
$a = ($error[0] | get-member -type property | select Name)
foreach($b in $a){
Invoke-Expression "`$error[0].$($b.Name)"
}
Just remove the &
$a = ($error[0] | get-member -type property | select Name)
foreach($b in $a){
$error[0].($b.Name)
}

How to refer to entire pipeline object without a script block

With the advent of PowerShell V3 instead of having to write:
Get-Process | Where { $_.ProcessName -match "win" }
...one could now write the more terse:
Get-Process | Where ProcessName -match "win"
... a clear win (ahem) for shell use.
Now let's say I had a simple array of strings, call it $stuff. Is it possible to reduce this:
$stuff | Where { $_ -match "win" }
...in an analogous way to the first example, i.e. removing the script block and referring to the entire object, in this case?
To reduce
$stuff | Where { $_ -match "win" }
you can always do it like this (works for all powershell version):
$stuff = "a","b","win", "winona", "d", "windows"
$stuff -match "win"
win
winona
windows
The form Get-Process | Where ProcessName -match "win" is called comparison statement. It's poorly a documented feature, as Where-Object's documentation doesn't really explain what those are about.
The reason comparison statement works for Get-Process but not for $stuff is that the former returns an array of System.Diagnostics.Process objects whlist the later is String. The comparison statements expects property name for filtering.
Let's look at what's available in each array member. First off processes like so,
$proc = get-process
gm -InputObject $proc[0] -MemberType property
TypeName: System.Diagnostics.Process
Name MemberType Definition
...
ProcessName Property string ProcessName {get;}
...
So, there is a ProcessName property, thus Where-Object can filter with it as seen.
The string array:
$stuff = #("foo", "bar", "zoffo", "qazzer")
gm -InputObject $stuff[0] -MemberType property
TypeName: System.String
Name MemberType Definition
Length Property int Length {get;}
There is only one property in a String which is it's length. Sure enough, it can be used for filtering like so,
$stuff | where length -ne 3
zoffo
qazzer
$stuff | where length -eq 3
foo
bar
As there are no other property typed members, the only way to filter the string array is by the classic script block mode.

How to find properties of an object?

How can I find what properties object $a has in the following?
$a = 1
$a.length
$a | Get-Member
Get-Member does not seem to produce any properties for object $a? Is length a property of object $a?
$a is an Integer, it doesn't have a length property. Using Get-member is the right way to find object properties.
You can also pipe a sample object to Select-Object to see all properties and their values.
get-process | select -first 1 -prop *

How to use the "-Property" parameter for PowerShell's "Measure-Object" cmdlet?

Why does
$a = GPS AcroRd32 | Measure
$a.Count
work, when
GPS AcroRd32 | Measure -Property Count
doesn't?
The first example returns a value of 2, which is what I want, an integer.
The second example returns this:
Measure-Object : Property "Count" cannot be found in any object(s) input.
At line:1 char:23
+ GPS AcroRd32 | Measure <<<< -Property Count
+ CategoryInfo : InvalidArgument: (:) [Measure-Object], PSArgumentException
+ FullyQualifiedErrorId : GenericMeasurePropertyNotFound,Microsoft.PowerShell.Commands.MeasureObjectCommand
This Scripting Guy entry is where I learned how to use the "Count" Property in the first code sample.
The second code sample is really confusing. In this Script Center reference, the following statement works:
Import-Csv c:\scripts\test.txt | Measure-Object score -ave -max -min
It still works even if it's re-written like so:
Import-Csv c:\scripts\test.txt | Measure-Object -ave -max -min -property score
I don't have too many problems with accepting this until I consider the Measure-Object help page. The parameter definition for -Property <string[]> states:
The default is the Count (Length) property of the object.
If Count is the default, then shouldn't an explicit pass of Count work?
GPS AcroRd32 | Measure -Property Count # Fails
The following provides me the information I need, except it doesn't provide me with an integer to perform operations on, as you'll see:
PS C:\Users\Me> $a = GPS AcroRd32 | Measure
PS C:\Users\Me> $a
Count : 2
Average :
Sum :
Maximum :
Minimum :
Property :
PS C:\Users\Me> $a -is [int]
False
So, why does Dot Notation ($a.count) work, but not an explicitly written statement (GPS | Measure -Property Count)?
If I'm supposed to use Dot Notation, then I will, but I'd like to take this opportunity to learn more about how and *why PowerShell works this way, rather than just building a perfunctory understanding of PowerShell's syntax. To put it another way, I want to avoid turning into a Cargo Cult Programmer/ Code Monkey.
Because the COUNT property is a property of the OUTPUT object (i.e. results of Measure-Object), not the INPUT object.
The -property parameter specifies which property(ies) of the input objects are to be evaluated. None of these is COUNT unless you pass an array or arrays or something.
I think what you want is something like this:
gps AcroRd32 | measure-object | select -expand Count
One thing you need to know is that in PowerShell generally, and particulary in CmdLets you manipulate objects or collection of objects.
Example: if only one 'AcroRd32' is running Get-Process will return a [System.Diagnostics.Process], if more than one are running it will return a collection of [System.Diagnostics.Process].
In the second case you can write:
(GPS AcroRd32).count
Because a collection has a count property. The duality object collection is also valid in CmdLets parameters that most of the time supports objects or list of objects (collection built with the operator ,).
PS C:\> (gps AcroRd32) -is [object[]]
True
Just use the Get-Member cmdlet:
PS C:\> (gps AcroRd32) | Get-Member
TypeName: System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------
Handles AliasProperty Handles = Handlecount
... ...
And
PS C:\> Get-Member -InputObject (gps AcroRd32)
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
... ...
If you're just looking for the count you can do the following:
$a = GPS AcroRd32
$a.Count = 2
$a = GPS AcroRd32 sets $a to an array of process objects. The array has a member call, Count, that will allow you to determine the number of elements already.
The Measure-Object commandlet (with alias measure) is used to measure the average, maximum, minimum, and sum values of a property. So you could do something like $a | measure -property Handles -sum and get a count of the total number of open handles.