I just wonder why I'm getting two different member lists when embedding an expression between parentheses, e.g. gl -stack. It seems that without the parenthesis, the expression is evaluated completely and the result passed at once to the next pipeline component. But with parenthesis, the single objects within the collection are passed one by one so that Get-Member is invoked for the objects in the collection instead for the collection itself.
Please see the following PowerShell interaction for an example with Get-Location -Stack.
Thanks in advance!
PS C:\temp\loc1> pushd
PS C:\temp\loc1> pushd ..\loc2
PS C:\temp\loc2> gl -stack
Path
----
C:\temp\loc1
C:\temp\loc1
PS C:\temp\loc2> gl -stack | gm
TypeName: System.Management.Automation.PathInfoStack
Name MemberType Definition
---- ---------- ----------
Clear Method System.Void Clear()
Contains Method bool Contains(System.Management.Automation.PathInfo...
CopyTo Method System.Void CopyTo(System.Management.Automation.Pat...
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.Generic.Stack`1+Enumerator[[Syst...
GetHashCode Method int GetHashCode()
GetType Method type GetType()
Peek Method System.Management.Automation.PathInfo Peek()
Pop Method System.Management.Automation.PathInfo Pop()
Push Method System.Void Push(System.Management.Automation.PathI...
ToArray Method System.Management.Automation.PathInfo[] ToArray()
ToString Method string ToString()
TrimExcess Method System.Void TrimExcess()
Count Property System.Int32 Count {get;}
Name Property System.String Name {get;}
PS C:\temp\loc2> (gl -stack) | gm
TypeName: System.Management.Automation.PathInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Drive Property System.Management.Automation.PSDriveInfo Drive {get;}
Path Property System.String Path {get;}
Provider Property System.Management.Automation.ProviderInfo Provider {...
ProviderPath Property System.String ProviderPath {get;}
Get-Location -Stack returns a PathInfoStack object as you have seen. That object is derived from Stack<T> which implements ICollection. When you put an expression inside () PowerShell evaluates that expression. If the result is a collection then it is iterated and output. You can see the same with this simple function:
PS> function GetArray() { ,#(1,2,3) }
PS> GetArray | Foreach {$_.GetType().FullName}
System.Object[]
PS> (GetArray) | Foreach {$_.GetType().FullName}
System.Int32
System.Int32
System.Int32
Related
I am trying to get a key using PowerShell that will uniquely identify a Windows computer that is relatively durable, i.e., doesn't change between re-installs of Windows.
I came across Windows.System.Profile.SystemIdentification which is part of the Windows UWP API but unfortunately so far, I have been unsuccessful in getting this with PowerShell.
How to get Windows 10 Device ID in UWP application?
I have been trying for the past several days and the farthest I have gone is the following:
PS> [Windows.System.Profile.SystemIdentification,Windows.System.Profile,ContentType=WindowsRuntime] | Out-Null
PS> $systemID = [Windows.System.Profile.SystemIdentification]::GetSystemIdForPublisher()
PS> $systemID
Id Source
-- ------
System.__ComObject Uefi
PS> $systemID | Get-Member
TypeName: Windows.System.Profile.SystemIdentificationInfo
Name MemberType Definition
---- ---------- ----------
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
ToString Method string ToString()
Id Property Windows.Storage.Streams.IBuffer Id {get;}
Source Property Windows.System.Profile.SystemIdentificationSource Source {get;}
How can I get the Id property which has a type of Windows.Storage.Streams.IBuffer Id {get;} ?
For this IBuffer you shuld use
Windows.Security.Cryptography.CryptographicBuffer.EncodeToHexString( systemID );
In case someone else comes across this, here's a working version of the info provided above...
# Use reflection syntax ( [class, namespace, contentType] ) to call GetSystemIdForPublisher()
$systemID = [Windows.System.Profile.SystemIdentification, Windows.System.Profile, ContentType = WindowsRuntime]::GetSystemIdForPublisher()
# And because PowerShell was designed for use by AI and people who think in hieroglyphics:
[Windows.Security.Cryptography.CryptographicBuffer, Windows.Security.Cryptography, ContentType = WindowsRuntime].
GetMethod('EncodeToHexString', [type[]]#([Windows.Storage.Streams.IBuffer, Windows.Storage.Streams, ContentType = WindowsRuntime])).
Invoke($null, #($systemID.Id))
Let's take an example. If understand correctly, System.IO.FileStream is a class, but [System.IO.FileStream] is an object: it's an instance of the class System.RuntimeType.
If I run [System.IO.FileStream] | Get-Member, I'll get the members of System.RuntimeType (technically, of its instance):
TypeName: System.RuntimeType
Name MemberType Definition
---- ---------- ----------
AsType Method type AsType()
Clone Method System.Object Clone()
Equals Method bool Equals(System.Object obj), bool Equals(type o)
FindInterfaces Method type[] FindInterfaces(System.Reflection.TypeFilter filter, System.Object filterCriteria)
...
TypeHandle Property System.RuntimeTypeHandle TypeHandle {get;}
TypeInitializer Property System.Reflection.ConstructorInfo TypeInitializer {get;}
UnderlyingSystemType Property System.Type UnderlyingSystemType {get;}
According to the documentation:
To get information about static members, the members of the class, not of the instance, use the Static parameter.
But if I run [System.IO.FileStream] | Get-Member -Static now I get:
TypeName: System.IO.FileStream
Name MemberType Definition
---- ---------- ----------
Equals Method static bool Equals(System.Object objA, System.Object objB)
ReferenceEquals Method static bool ReferenceEquals(System.Object objA, System.Object objB)
Synchronized Method static System.IO.Stream Synchronized(System.IO.Stream stream)
Null Property static System.IO.Stream Null {get;}
Q1. These are the static members of System.IO.FileStream, not System.RuntimeType. Why is that?
The documentation also says:
We use :: to indicate to Windows PowerShell that we want to work with a static method or property.
This made me realize that [System.IO.FileStream]::Null refers to the static property Null of the class System.IO.FileStream.
Q2. Does it mean that Powershell sometimes sees [System.IO.FileStream] as the class System.IO.FileStream (when dealing with static members), and sometimes as an instance of System.RuntimeType?
By default, Get-Member without -Static always acts on the type of its input objects.
[System.IO.FileStream] is an instance of a PowerShell type literal ([<type-name>]), whose type is System.RuntimeType, which is a PowerShell-internal class that derives from System.Reflection.TypeInfo and ultimately System.Type.
Therefore, [System.IO.FileStream] | Get-Member outputs information about the members of System.RuntimeType - and so would any type literal (e.g., [int] | Get-Member would yield the same output).
By contrast, Get-Member with -Static:
acts on the input object itself if it happens to be a System.RunTimeType instance, such as type literal [System.IO.FileStream]; otherwise, as usual, the input object's type is operated on.
reports only the static members of the input object / type.
Therefore, [System.IO.FileStream] | Get-Member -Static reports the static members of type System.IO.FileStream - as would passing an instance of type System.IO.FileStream; e.g., [System.IO.FileStream]::new($PROFILE, 'Open') | Get-Member -Static
A curious omission, as of PowerShell Core 7.0, is that you cannot use Get-Member to request instance members by type; the only way to get a type's instance members is to pass an actual instance as input; however, the challenge is that an instance is not always easy to come by, so getting instance members by type would be helpful,
which this GitHub issue requests.
In the meantime, you can call .GetMembers() on a type, as you demonstrate in your own answer here.
Suppose $coll is a collection of objects. $coll | Get-Member displays the members of a collection item. I do not know how it works exactly, whether it inspects the first item and stops or it shows the union of members of all the items. Nevertheless, it inspects the items, not the collection object itself. Which is usually, what we want.
But what if I want to inspect the collection itself? Can I still do it or must I use the .NET reflection functions?
Stupid me, of course it can. I just have to use the -InputObject parameter, instead of pipeline - Get-Member -InputObject $coll instead of $coll | Get-Member
C:\WINDOWS\System32> $x = #([pscustomobject]#{a = 1})
C:\WINDOWS\System32> $x|Get-Member
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
a NoteProperty int a=1
C:\WINDOWS\System32> Get-Member -InputObject $x
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
Add Method int IList.Add(System.Object value)
Address Method System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a...
Clear Method void IList.Clear()
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
CompareTo Method int IStructuralComparable.CompareTo(System.Object other, System.Collections.ICo...
Contains Method bool IList.Contains(System.Object value)
CopyTo Method void CopyTo(array array, int index), void CopyTo(array array, long index), void...
Equals Method bool Equals(System.Object obj), bool IStructuralEquatable.Equals(System.Object ...
Get Method System.Object Get(int )
GetEnumerator Method System.Collections.IEnumerator GetEnumerator(), System.Collections.IEnumerator ...
GetHashCode Method int GetHashCode(), int IStructuralEquatable.GetHashCode(System.Collections.IEqu...
GetLength Method int GetLength(int dimension)
GetLongLength Method long GetLongLength(int dimension)
GetLowerBound Method int GetLowerBound(int dimension)
GetType Method type GetType()
GetUpperBound Method int GetUpperBound(int dimension)
GetValue Method System.Object GetValue(Params int[] indices), System.Object GetValue(int index)...
IndexOf Method int IList.IndexOf(System.Object value)
Initialize Method void Initialize()
Insert Method void IList.Insert(int index, System.Object value)
Remove Method void IList.Remove(System.Object value)
RemoveAt Method void IList.RemoveAt(int index)
Set Method void Set(int , System.Object )
SetValue Method void SetValue(System.Object value, int index), void SetValue(System.Object valu...
ToString Method string ToString()
Item ParameterizedProperty System.Object IList.Item(int index) {get;set;}
IsFixedSize Property bool IsFixedSize {get;}
IsReadOnly Property bool IsReadOnly {get;}
IsSynchronized Property bool IsSynchronized {get;}
Length Property int Length {get;}
LongLength Property long LongLength {get;}
Rank Property int Rank {get;}
SyncRoot Property System.Object SyncRoot {get;}
C:\WINDOWS\System32>
According to the documentation for the parameters to Get-Member:
-InputObject
Specifies the object whose members are retrieved.
Using the InputObject parameter is not the same as piping an object to Get-Member. The differences are as follows:
When you pipe a collection of objects to Get-Member, Get-Member gets the members of the individual objects in the collection, such as the properties of each string in an array of strings.
When you use InputObject to submit a collection of objects, Get-Member gets the members of the collection, such as the properties of the array in an array of strings.
so $coll | Get-Member will invoke Get-Member on each item in the collection, but Get-Member -InputObject $coll will invoke Get-Member on the collection object itself.
However, if you're a fan of using the pipeline you can use a cheap trick to coerce PowerShell into doing the former with a collection object by wrapping it in a sacrificial array (name borrowed from this answer) containing a single element:
#( ,$coll ) | Get-Member
and PowerShell will happily unwrap the outer array and invoke Get-Member for each child item - in this case your original collection object.
Can anyone explain this strange powershell behavior.
If I declare the following:
$MyCollection = New-Object System.Collections.ArrayList
$MyCollection.Add(#{'Val1'=1; 'Val2'=2; 'Val3'=3})
$MyCollection.Add(#{'Val1'=1; 'Val2'=2; 'Val3'=3})
$MyCollection.Add(#{'Val1'=1; 'Val2'=2; 'Val3'=3})
So... I should have an object that looks like this:
System.Collections.Array.List
Index Item
0 {System.Collections.HashTable}
1 {System.Collections.HashTable}
2 {System.Collections.HashTable}
However if I get member you'll see my entire colleciton has become one large hash table
$MyCollection | Get-Member
TypeName: System.Collections.Hashtable
Name MemberType Definition
---- ---------- ----------
Add Method void Add(System.Object key, System.Object value), void IDictionary.Add(System.Object key, System.Object value)
Clear Method void Clear(), void IDictionary.Clear()
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
Contains Method bool Contains(System.Object key), bool IDictionary.Contains(System.Object key)
ContainsKey Method bool ContainsKey(System.Object key)
ContainsValue Method bool ContainsValue(System.Object value)
CopyTo Method void CopyTo(array array, int arrayIndex), void ICollection.CopyTo(array array, int index)
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.IDictionaryEnumerator GetEnumerator(), System.Collections.IDictionaryEnumerator IDictionary.GetEnumerator(), System.Collections.IEnumerator IEnumerable.GetEn...
GetHashCode Method int GetHashCode()
GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), void ISerializable.GetObjectData(System.Runtime....
GetType Method type GetType()
OnDeserialization Method void OnDeserialization(System.Object sender), void IDeserializationCallback.OnDeserialization(System.Object sender)
Remove Method void Remove(System.Object key), void IDictionary.Remove(System.Object key)
ToString Method string ToString()
Item ParameterizedProperty System.Object Item(System.Object key) {get;set;}
Count Property int Count {get;}
IsFixedSize Property bool IsFixedSize {get;}
IsReadOnly Property bool IsReadOnly {get;}
IsSynchronized Property bool IsSynchronized {get;}
Keys Property System.Collections.ICollection Keys {get;}
SyncRoot Property System.Object SyncRoot {get;}
Values Property System.Collections.ICollection Values {get;}
And the behavior is like this. For instance I can't access my object the way I want to:
$MyCollection | %{$_.Val1}
outputs
1
1
1
Expected output
1
As you can see we have one large hash table now with very strange behavior. Can anyone explain what Powershell is actually doing? Because it is defiantly not accessing a HashTable inside an ArrayList collection.
its like calling any cmdlets flattens my data structure into a single hash table
When you pass an array via the pipeline it is unrolled so in your example Get-Member is seeing the internals of the array, the hashtables.
To get the type of an object that is an array you can use the GetType() method and look to the name property.
$MyCollection.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True ArrayList System.Object
Ansgar Wiechers showed below that you can also use the -InputObject parameter to get the type of an array.
Get-Member -InputObject $MyCollection
If you want only a single item from the array of hashtables output you'll need to specify which index in the array you want the value followed by the hashtable key.
$MyCollection[0]['Val1']
1
The example above returns the value stored in the key Val1 of the first object in the array. To get the others you'll have to increment the index number or change the key.
I'm using Chad Miller & Hey Scripting Guy's functions invoke-sqlcmd2 and write-datatable to save the results of a query to a datatable, then write it into a SQL table.
Here's the code I'm running:
$dt = invoke-sqlcmd2 -serverinstance $_.server -query "exec master.dbo.sp_who" -As 'Datatable'
write-datatable -serverinstance "myserverhere" -DATABASE "dbainfo" -tablename "who_sp_results" -DATA $dt
I want to add a field to the datatable - I'm running an SP I can't modify, and want to add the servername. I'm running it in parallel, so I can't use a staging table, I have to either modify the existing datatable or move it to a new datatable.
How do I copy/alter a datatable to get the additional column and set it to another variable?
If we use a second datatable, since I'm looping, how do I "truncate"
it or clear it out prior to the next run within the foreach loop?
UPDATE: added one row from $dt | select-object * , and $dt | get-member
spid : 1
ecid : 0
status : background
loginame : sa
hostname :
blk : 0
dbname :
cmd : RESOURCE MONITOR
request_id : 0
RowError :
RowState : Unchanged
Table : {sa, sa, sa, sa...}
ItemArray : {1, 0, background , sa...}
HasErrors : False
Here's the results from $dt | get-member :
TypeName: System.Data.DataRow
Name MemberType Definition
---- ---------- ----------
AcceptChanges Method System.Void AcceptChanges()
BeginEdit Method System.Void BeginEdit()
CancelEdit Method System.Void CancelEdit()
ClearErrors Method System.Void ClearErrors()
Delete Method System.Void Delete()
EndEdit Method System.Void EndEdit()
Equals Method bool Equals(System.Object obj)
GetChildRows Method System.Data.DataRow[] GetChildRows(string relationName), System.Data.DataRow[] GetChildRows(...
GetColumnError Method string GetColumnError(int columnIndex), string GetColumnError(string columnName), string Get...
GetColumnsInError Method System.Data.DataColumn[] GetColumnsInError()
GetHashCode Method int GetHashCode()
GetParentRow Method System.Data.DataRow GetParentRow(string relationName), System.Data.DataRow GetParentRow(stri...
GetParentRows Method System.Data.DataRow[] GetParentRows(string relationName), System.Data.DataRow[] GetParentRow...
GetType Method type GetType()
HasVersion Method bool HasVersion(System.Data.DataRowVersion version)
IsNull Method bool IsNull(int columnIndex), bool IsNull(string columnName), bool IsNull(System.Data.DataCo...
RejectChanges Method System.Void RejectChanges()
SetAdded Method System.Void SetAdded()
SetColumnError Method System.Void SetColumnError(int columnIndex, string error), System.Void SetColumnError(string...
SetModified Method System.Void SetModified()
SetParentRow Method System.Void SetParentRow(System.Data.DataRow parentRow), System.Void SetParentRow(System.Dat...
ToString Method string ToString()
Item ParameterizedProperty System.Object Item(int columnIndex) {get;set;}, System.Object Item(string columnName) {get;s...
blk Property System.String blk {get;set;}
cmd Property System.String cmd {get;set;}
dbname Property System.String dbname {get;set;}
ecid Property System.Int16 ecid {get;set;}
hostname Property System.String hostname {get;set;}
loginame Property System.String loginame {get;set;}
request_id Property System.Int32 request_id {get;set;}
spid Property System.Int16 spid {get;set;}
status Property System.String status {get;set;}
You don't want to use add-member as this won't add a type of datacolumn. Instead do the following:
$Col = new-object Data.DataColumn
$Col.ColumnName = "ServerName"
$dt.Columns.Add($col)
$dt | %{$_.ServerName = "myserverName"}
BTW if you use get-member with inputobject parameter you'll see the columns and add method:
gm -inputobject $dt
This is different than
$dt | gm which returns properties and methods of datarows. The first one doesn't unroll.
I think you are looking for Add-Member