Powershell - copying or adding to a Datatable? - tsql

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

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

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.

System.Collections.ArrayList becomes System.Collecitons.HashTable

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.

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