System.Collections.ArrayList becomes System.Collecitons.HashTable - powershell

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.

Related

Change the output of the [Environment]::GetEnvironmentVariables('Machine') command in powershell OR get the list of the names of the environment var

I'm trying to list the system environment variables in Powershell, and to change the output to something like :
NAME=VALUE
NUMBER_OF_PROCESSORS=8
TMP=C:\WINDOWS\TEMP
So something with name=value and a return to the line after all variable
The problem is that I can't make this output, the only thing I can found is :
Name : NUMBER_OF_PROCESSORS
Value : 8
Name : windir
Value : C:\WINDOWS
using the command :
[Environment]::GetEnvironmentVariables('Machine') | Format-List
This first one is almost what I want but it's still not perfect.
Or I can also have :
Name Value
---- -----
NUMBER_OF_PROCESSORS 8
TMP C:\WINDOWS\TEMP
windir C:\WINDOWS
using the command :
[Environment]::GetEnvironmentVariables('Machine') | Format-Table
Which is badder than the first one because the line are cut (the path is not fully displayed because its to large to be printed on the screen)
I tried to put the output in a variable and only get the name of the variables but it still doesn't work :
PS> $array=[Environment]::GetEnvironmentVariables('Machine')
PS> $array[0].Name
and also tried "Get-Member" to get all the variable names but it don't show what I need :
PS> $array[0] | Get-Member
TypeName : System.Collections.Hashtable
Name MemberType Definition
---- ---------- ----------
Add Method void Add(System.Object key, System.Object value), void IDiction...
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(Sys...
ContainsKey Method bool ContainsKey(System.Object key)
ContainsValue Method bool ContainsValue(System.Object value)
CopyTo Method void CopyTo(array array, int arrayIndex), void ICollection.Copy...
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.IDictionaryEnumerator GetEnumerator(), Syste...
GetHashCode Method int GetHashCode()
GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationIn...
GetType Method type GetType()
OnDeserialization Method void OnDeserialization(System.Object sender), void IDeserializa...
Remove Method void Remove(System.Object key), void IDictionary.Remove(System....
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;}
I also found that if you type :
$array[0].TMP
It shows you the value of the variable which is in my case :
C:\WINDOWS\TEMP
In that case I want to get the list of the variable name to make a loop which call $array[0].Name, where name is the name of the variable (and i did not code it yet because I can't get this list)
So my questions are :
How can I get the list of the environment variable names (TEMP, USERNAME, etc) ?
How can I change my output to make it looks like name=value ?
Since GetEnvironmentVariables outputs IDictionary you can enumerate each Key / Value pair using the GetEnumerator Method:
[System.Environment]::GetEnvironmentVariables('Machine').GetEnumerator() | ForEach-Object {
'{0}={1}' -f $_.Key, $_.Value
}
If you would like to list only a specific set of Environment Variables you could change the logic and use GetEnvironmentVariable instead to target specific ones:
$variablesOfInterest = 'TEMP', 'USERNAME'
$variablesOfInterest | ForEach-Object {
'{0}={1}' -f $_, [System.Environment]::GetEnvironmentVariable($_)
}
Regarding:
How can I get the list of the environment variable names (TEMP, USERNAME, etc) ?
Same as with any other type implementing IDictionary, you can call it's .Keys property:
[System.Environment]::GetEnvironmentVariables('Machine').Keys

PnP - PowerShell - SharePoint - Getting all files in a library but omitting folder items

I'm using the PnP PowerShell module to interact with SharePoint lists/libraries. I'm running the following cmdlet to get a list of all the files in a library. This is working perfectly, except it returns all any folders in the library too. Is there anyway to omit folder items? I still want the files within the folders to be returned but just not the folders.
$files = (Get-PnPListItem -List $docLib.Title -Fields FileLeafRef","FileRef","Title").FieldValues
I suspect I can add a Where clause, however I'm not sure what property would identify an item as a folder. Any guidance would be appreciated. Thanks!
Many thanks
UPDATED: $files[0] | gm returns:
Add Method void Add(string key, System.Object value), void IDictionary[string,Object].Add(string key, System.Object value), void ICollection[KeyValuePair[stri...
Clear Method void Clear(), void ICollection[KeyValuePair[string,Object]].Clear(), void IDictionary.Clear()
Contains Method bool ICollection[KeyValuePair[string,Object]].Contains(System.Collections.Generic.KeyValuePair[string,System.Object] item), bool IDictionary.Contai...
ContainsKey Method bool ContainsKey(string key), bool IDictionary[string,Object].ContainsKey(string key), bool IReadOnlyDictionary[string,Object].ContainsKey(string key)
ContainsValue Method bool ContainsValue(System.Object value)
CopyTo Method void ICollection[KeyValuePair[string,Object]].CopyTo(System.Collections.Generic.KeyValuePair[string,System.Object][] array, int arrayIndex), void I...
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.Generic.Dictionary`2+Enumerator[string,System.Object] GetEnumerator(), System.Collections.Generic.IEnumerator[System.Collections...
GetHashCode Method int GetHashCode()
GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), void ISerializable....
GetType Method type GetType()
OnDeserialization Method void OnDeserialization(System.Object sender), void IDeserializationCallback.OnDeserialization(System.Object sender)
Remove Method bool Remove(string key), bool IDictionary[string,Object].Remove(string key), bool ICollection[KeyValuePair[string,Object]].Remove(System.Collection...
ToString Method string ToString()
TryGetValue Method bool TryGetValue(string key, [ref] System.Object value), bool IDictionary[string,Object].TryGetValue(string key, [ref] System.Object value), bool I...
Item ParameterizedProperty System.Object Item(string key) {get;set;}, System.Object IDictionary.Item(System.Object key) {get;set;}
Comparer Property System.Collections.Generic.IEqualityComparer[string] Comparer {get;}
Count Property int Count {get;}
IsFixedSize Property bool IsFixedSize {get;}
IsReadOnly Property bool IsReadOnly {get;}
IsSynchronized Property bool IsSynchronized {get;}
Keys Property System.Collections.Generic.Dictionary`2+KeyCollection[string,System.Object] Keys {get;}
SyncRoot Property System.Object SyncRoot {get;}
Values Property System.Collections.Generic.Dictionary`2+ValueCollection[string,System.Object] Values {get;}
UPDATED: $files[0] returns the following (you can see it's returns a folder called 'General'):
FileLeafRef General
FileRef /sites/Finance/Shared Documents/General
Title General
HTML_x0020_File_x0020_Type Team.Channel
MetaInfo vti_isexecutable:BW|false...
_ModerationStatus 0
_Level 1
Last_x0020_Modified 2022-04-03T19:52:10Z
ID 1
UniqueId 10d788d1-0308-4768-b47b-33f79723386e
owshiddenversion 2
FSObjType 1
Created_x0020_Date 2022-01-26T10:35:55Z
ProgId Team.Channel
Modified 1/26/2022 10:35:56 AM
CheckoutUser
ScopeId {3E95DFBF-DEF4-4FC8-AE50-C84D4E2F4483}
Editor Microsoft.SharePoint.Client.FieldUserValue
UPDATED: Here are the properties of a file, the FSObjType is different, looks like 1 for folders, and 0 for files:
FileLeafRef Document.docx
FileRef /sites/Finance/Shared Documents/General/Document.docx
Title test
HTML_x0020_File_x0020_Type
MetaInfo vti_mediaservicemetadata:SW|{"ctag":"\\"c:{7799CB41-34F1-40F5-A701-BFF6AAC846D0},5\\"","generationTime":"2022-01-26T12:13:38.1337171Z","buildVersion":"Media_PRO...
_ModerationStatus 0
_Level 1
Last_x0020_Modified 2022-01-27T09:04:07Z
ID 2
UniqueId 7799cb41-34f1-40f5-a701-bff6aac846d0
owshiddenversion 6
FSObjType 0
Created_x0020_Date 2022-01-26T12:12:31Z
ProgId
Modified 1/27/2022 9:04:06 AM
CheckoutUser
ScopeId {3E95DFBF-DEF4-4FC8-AE50-C84D4E2F4483}
Editor Microsoft.SharePoint.Client.FieldUserValue
So I've updated my call to the following which is now working perfectly, thanks Toni!
$files = (Get-PnPListItem -List $docLib.Title -Fields "FileLeafRef","Title").FieldValues | Where {$_.FSObjType -eq "0"}
Please see updated description, FSObjType will allow you to pull back files or folders (0 for files, 1 for folders)

How to read timezone id from GET-TIMEZONE function response in powershell

I am new to powershell, so do not know what is the best way to read id of timezone from the GET-TIMEZONE function response in powershell.
The function returns response in this format
I want to read the Id value. Which is India Standard Time in this example.
Any leads?
As requested by Matt Johnson-Pint here my comment as answer.
The Get-TimeZone cmdlet returns the current time zone or a list of available time zones. Without using parameters, it returns the timezone for the local computer.
The returned value(s) are always object(s) with properties as shown in the OPs screenshot.
If you want the value of just one of these properties. you can use either the dot syntax
(Get-TimeZone).Id
which because of the brackets, makes PowerShell execute the cmdlet first and from that returned object, you pick the apropriate property to get its value.
Another way is to use Select-Object and expand the property you need in order to receive the value only like this:
Get-TimeZone | Select-Object -ExpandProperty Id
Without parameter -ExpandProperty, this will return a new object with one property named Id.
Objects usually also have methods besides properties. To show these, you can pipe the result through to another cmdlet called Get-Member (or gm for short)
Get-TimeZone | Get-Member * # using the asteriks, the cmdlet returns all members
which results in
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.TimeZoneInfo other), bool Equals(System.Object obj), bool IEquatable[TimeZoneInfo].E...
GetAdjustmentRules Method System.TimeZoneInfo+AdjustmentRule[] GetAdjustmentRules()
GetAmbiguousTimeOffsets Method timespan[] GetAmbiguousTimeOffsets(System.DateTimeOffset dateTimeOffset), timespan[] GetAmbiguousTimeOf...
GetHashCode Method int GetHashCode()
GetObjectData Method void ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Se...
GetType Method type GetType()
GetUtcOffset Method timespan GetUtcOffset(System.DateTimeOffset dateTimeOffset), timespan GetUtcOffset(datetime dateTime)
HasSameRules Method bool HasSameRules(System.TimeZoneInfo other)
IsAmbiguousTime Method bool IsAmbiguousTime(System.DateTimeOffset dateTimeOffset), bool IsAmbiguousTime(datetime dateTime)
IsDaylightSavingTime Method bool IsDaylightSavingTime(System.DateTimeOffset dateTimeOffset), bool IsDaylightSavingTime(datetime dat...
IsInvalidTime Method bool IsInvalidTime(datetime dateTime)
OnDeserialization Method void IDeserializationCallback.OnDeserialization(System.Object sender)
ToSerializedString Method string ToSerializedString()
ToString Method string ToString()
BaseUtcOffset Property timespan BaseUtcOffset {get;}
DaylightName Property string DaylightName {get;}
DisplayName Property string DisplayName {get;}
Id Property string Id {get;}
StandardName Property string StandardName {get;}
SupportsDaylightSavingTime Property bool SupportsDaylightSavingTime {get;}

Can Get-Member be used to inspect methods and properties of a collection, rather than a collection item (the default)?

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.

Difference between "(gl -stack)" and "gl -stack"

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