I have (yet another) powershell query. I have an array in powershell which i need to use the remove() and split commands on.
Normally you set an array (or variable) and the above methods exist. On the below $csv2 array both methods are missing, i have checked using the get-member cmd.
How can i go about using remove to get rid of lines with nan. Also how do i split the columns into two different variables. at the moment each element of the array displays one line, for each line i need to convert it into two variables, one for each column.
timestamp Utilization
--------- -----------
1276505880 2.0763250000e+00
1276505890 1.7487730000e+00
1276505900 1.6906890000e+00
1276505910 1.7972880000e+00
1276505920 1.8141900000e+00
1276505930 nan
1276505940 nan
1276505950 0.0000000000e+00
$SystemStats = (Get-F5.iControl).SystemStatistics
$report = "c:\snmp\data" + $gObj + ".csv"
### Allocate a new Query Object and add the inputs needed
$Query = New-Object -TypeName iControl.SystemStatisticsPerformanceStatisticQuery
$Query.object_name = $i
$Query.start_time = $startTime
$Query.end_time = 0
$Query.interval = $interval
$Query.maximum_rows = 0
### Make method call passing in an array of size one with the specified query
$ReportData = $SystemStats.get_performance_graph_csv_statistics( (,$Query) )
### Allocate a new encoder and turn the byte array into a string
$ASCII = New-Object -TypeName System.Text.ASCIIEncoding
$csvdata = $ASCII.GetString($ReportData[0].statistic_data)
$csv2 = convertFrom-CSV $csvdata
$csv2
There is no Remove or Split method on .NET's Array type, or added by the PowerShell wrapper around an Array instance. This is quite easy to show:
PS[64bit] E:\> $a = 1,2,3,4,5
PS[64bit] E:\> $a.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS[64bit] E:\> Get-Member -InputObject $a
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
Address Method System.Object&, mscorlib, Version=2.0.0.0, Culture=neutral, PublicK...
Clone Method System.Object Clone()
CopyTo Method System.Void CopyTo(array array, int index), System.Void CopyTo(arra...
Equals Method bool Equals(System.Object obj)
Get Method System.Object Get(int )
GetEnumerator Method System.Collections.IEnumerator GetEnumerator()
GetHashCode Method int GetHashCode()
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 GetValu...
Initialize Method System.Void Initialize()
Set Method System.Void Set(int , System.Object )
SetValue Method System.Void SetValue(System.Object value, int index), System.Void S...
ToString Method string ToString()
IsFixedSize Property System.Boolean IsFixedSize {get;}
IsReadOnly Property System.Boolean IsReadOnly {get;}
IsSynchronized Property System.Boolean IsSynchronized {get;}
Length Property System.Int32 Length {get;}
LongLength Property System.Int64 LongLength {get;}
Rank Property System.Int32 Rank {get;}
Arrays in .NET, and PowerShell, are fixed size. To remove an element you need to copy all but the element to be removed, in PSH this can be done with Where-Object:
$newArray = $oldArray | Where-Object {some-condition-on-$_}
Similarly Select-Object with -First and -Skip parameters can be used to select elements before or after (resp3ectively) an index.
NB System.Array does implement System.Collections.ILst but the explicit implementation of IList.Remove just throws a NotImplementedException.
Related
I need to look through eventLog security ID 4648, and find the last time the user connected to the machine.
Currently this is my code:
$Values = invoke-command -ComputerName $ComputerName {Get-EventLog -LogName Security -InstanceID 4648 | Select-Object -ExpandProperty Message| ForEach-Object {if($_.Log -match "$String2"){
$_
Break }}}
$Values
The aim was to go through each log until a log where the message has the previously defined username is found, and then stop going through EventLog and return that log.
This is working well, except its not matching the correct log with the specified string.
Is there a way to improve how the matching works? So it actually finds the correct log with the specified user?
# Fill in the regex for the userName
$userName = "userName"
$Values = #(invoke-command -ComputerName $ComputerName {
Get-EventLog -LogName Security -InstanceID 4648 | Where-Object { $_.message -match $Using:userName } | Select-Object -First 1)
}
Your above sample won't work since message is of type string, therefore it doesn't have a Log property. Since we want $userName to be avaiable for read access on the remote machine we can use the $Using: syntax. To break the pipeline "iteration" I'm using Select-Object -First 1 which will return the first object passing the Where-Objectclause.
Resulting from that $Values points to a collection of (deserialized) objects (using the #() operator) of type:
TypeName: System.Diagnostics.EventLogEntry#Security/Microsoft-Windows-Security-Auditing/4648
Which means you can change the -First parameter to e.g. 10 and sort the result on the client machine:
$Values | sort TimeGenerated -Descending
If you want to know which properties are available you can use:
> $Values | gm
TypeName: System.Diagnostics.EventLogEntry#Security/Microsoft-Windows-Security-Auditing/4648
Name MemberType Definition
---- ---------- ----------
Disposed Event System.EventHandler Disposed(System.Object, System.EventArgs)
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Dispose Method void Dispose(), void IDisposable.Dispose()
Equals Method bool Equals(System.Diagnostics.EventLogEntry otherEntry), bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetObjectData Method void ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
ToString Method string ToString()
Category Property string Category {get;}
CategoryNumber Property int16 CategoryNumber {get;}
Container Property System.ComponentModel.IContainer Container {get;}
Data Property byte[] Data {get;}
EntryType Property System.Diagnostics.EventLogEntryType EntryType {get;}
Index Property int Index {get;}
InstanceId Property long InstanceId {get;}
MachineName Property string MachineName {get;}
Message Property string Message {get;}
ReplacementStrings Property string[] ReplacementStrings {get;}
Site Property System.ComponentModel.ISite Site {get;set;}
Source Property string Source {get;}
TimeGenerated Property datetime TimeGenerated {get;}
TimeWritten Property datetime TimeWritten {get;}
UserName Property string UserName {get;}
EventID ScriptProperty System.Object EventID {get=$this.get_EventID() -band 0xFFFF;}
Hope that helps.
I would have expected to get the same "type" from both of the following commands. The second prepends the type name with "Selected."
>(Get-CimInstance CIM_LogicalDisk).CimClass | gm
TypeName: Microsoft.Management.Infrastructure.CimClass
Name MemberType Definition
---- ---------- ----------
Dispose Method void Dispose(), void IDisposable.Dispose()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
CimClassMethods Property Microsoft.Management.Infrastructure.Generic.CimReadOnlyKeyedCollection[Microsoft.M
CimClassProperties Property Microsoft.Management.Infrastructure.Generic.CimReadOnlyKeyedCollection[Microsoft.M
CimClassQualifiers Property Microsoft.Management.Infrastructure.Generic.CimReadOnlyKeyedCollection[Microsoft.M
CimSuperClass Property cimclass CimSuperClass {get;}
CimSuperClassName Property string CimSuperClassName {get;}
CimSystemProperties Property Microsoft.Management.Infrastructure.CimSystemProperties CimSystemProperties {get;}
CimClassName ScriptProperty System.String CimClassName {get=[OutputType([string])]...
The second reveals a different type.
>Get-CimInstance CIM_LogicalDisk | Select-Object -Property CimClass | gm
TypeName: Selected.Microsoft.Management.Infrastructure.CimInstance
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
CimClass NoteProperty cimclass CimClass=root/cimv2:Win32_MappedLogicalDisk
>$PSVersionTable.PSVersion.ToString()
5.1.14409.1018
Using Select-Object with the -Property parameter outputs an object with the selected properties.
To get a "bare" property value, use the -ExpandProperty parameter instead.
Get-CimInstance CIM_LogicalDisk | Select-Object -ExpandProperty CimClass | gm
I am setting the variable 'a' like this:
$a=dir -recurse c:\temp
If I now examine this object with 'get-member' like this:
$a|get-member
I get back the type, but also all the methods and other properties like this:
TypeName: System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
Mode CodeProperty System.String Mode{get=Mode;}
AppendText Method System.IO.StreamWriter AppendText()
CopyTo Method System.IO.FileInfo CopyTo(string destFileName), System.IO.FileInfo CopyTo(string destFileName, bool...
[...]
Which is nice; but sometimes I just want to get hold of the type (I'll look up whatever it does afterwards).
So I tried this:
$a|get-member|select-object -Property typename
The output suprised me initially: because what you get back is the typename for each individual item in the collection- and the types (although clearly related) are not identical:
TypeName
--------
System.IO.DirectoryInfo
System.IO.DirectoryInfo
System.IO.DirectoryInfo
[...]
System.IO.FileInfo
[...]
Then I thought about this; and it sort of made sense - this is a collection of Objects that I'm piping through the Object-Pipeline; but then it made me think:
What was 'Get-Member' actually telling me previously ? When it said the type was 'System.IO.FileInfo' - but actually the collection contains a mixture of object types ?
Whatever it is 'Get-Member' is displaying - how do I get at that exact thing ?
I can almost (sort-of, but actually its just wrong) get what I thought I was initially after with this:
$a|get-member|select-object -Property typename -first 1
But this just peeks at the 'first' object; and in fact gives me a different answer to what 'Get-Member' output for me.
So what is the 'TypeName' that 'Get-Member' is showing- and where is that stored ?
Is the value of 'dir' (Get-ChildItem against a filepath) simply a collection of objects, or is it a parent object (with its own set of 'scalar' properties) and a single property referencing a collection of associated objects ?
That's a lot of questions in one, let's see if we can make some sense of this.
The TypeName that Get-Member displays for each distinct type of object (we'll get back to that), comes from a hidden property that all objects in PowerShell carry, called pstypenames:
PS C:\> $something = 1
PS C:\> $something.pstypenames
System.Int32
System.ValueType
System.Object
PS C:\>
So, pstypenames is an ordered list of all the types in that objects type hierarchy. If we change the value of pstypenames, you'll see that reflected in the output from Get-Member:
PS C:\> $something.pstypenames.Insert(0,"MonoJohnny.CustomTypeName")
PS C:\> Get-Member -InputObject $something
TypeName: MonoJohnny.CustomTypeName
Name MemberType Definition
---- ---------- ----------
...
So, if you want the TypeName for an object, as displayed by Get-Member, you can always do:
$something.pstypenames[0]
As shown above, this value can be manipulated, so if you want the true type of an object at runtime, use the GetType() method:
$something.GetType().FullName
The reason that Get-Member only shows you the entire list of properties for a System.IO.FileInfo object once is that it (rightly so) assumes that all other objects of type System.IO.FileInfo will have the exact same members - no need to duplicate that output.
I you have multiple distinct types in a collection and pipe those to Get-Member, it'll only show you the members for the first object it encounters with a unique type name (remember, the value of pstypenames[0]). This is the case when you pipe Get-ChildItem to Get-Member, since Get-ChildItem on the filesystem provider only returns two different types of objects - FileInfo objects and DirectoryInfo objects.
For builtin types this is totally fine, but with custom objects that you create in PowerShell, this can be quite annoying.
Consider the following example:
PS C:\> $Object1 = New-Object psobject -Property #{ Prop1 = 1 }
PS C:\> $Object2 = New-Object psobject -Property #{ Prop2 = 2 }
Now, $Object1 and $Object2 are clearly 2 different kinds of objects - they have different property names. But what happens when we pipe them to Get-Member in the same pipeline:
PS C:\> $Object1,$Object2 |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()
Prop1 NoteProperty System.Int32 Prop1=1
Since the underlying type of both objects are System.Management.Automation.PSObject, the value of pstypenames is also the same for both, and Get-Member can't distinguish between the two.
Now, all of a sudden, the ability to manipulate pstypenames without actually fiddling with the type system comes in handy:
PS C:\> $Object1.pstypenames.Insert(0,"ObjectType1")
PS C:\> $Object2.pstypenames.Insert(0,"ObjectType2")
PS C:\> $Object1,$Object2 |Get-Member
TypeName: ObjectType1
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Prop1 NoteProperty System.Int32 Prop1=1
TypeName: ObjectType2
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Prop2 NoteProperty System.Int32 Prop2=2
Sweet!
You can also inject a custom type name during object creation by specifying PSTypeName as a string property:
PS C:\> $Object1 = New-Object psobject -Property #{ Prop1 = 1; PSTypeName = 'ObjectType1' }
PS C:\> $Object2 = New-Object psobject -Property #{ Prop2 = 2; PSTypeName = 'ObjectType2' }
The following shows that the returned type are different depends on how many rows returned. Why it's designed this way? It's very easy to make assumption it always returns an array and write the code $a.Length $a | % { ....} which will raise error when it returns only one row, unless the it's written as $a = #(invoke-....), which is easy to forget.
$a=Invoke-Sqlcmd -ServerInstance server "select 1 a"
$b=Invoke-Sqlcmd -ServerInstance server "select 1 a union all select 2"
$a.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False DataRow System.Object
And the following statement returns an array of object (BTW, why not an array of DataRow?)
$b.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
However, gm returns the same type for both variables. Why it's designed this way which can be very confused.
Question:
What's the point that the array is removed when only one item is returned?
Why gm get item type of an array? How to gm of an array?
Why getType() cannot return the data type of the item when it returns an array type?
?
PS SQLSERVER:\> $a|gm
TypeName: System.Data.DataRow
Name MemberType Definition
---- ---------- ----------
AcceptChanges Method void AcceptChanges()
......
ToString Method string ToString()
Item ParameterizedProperty System.Object Item(int columnIndex) {get;set;}, System.Object Item(string co...
a Property int a {get;set;}
PS SQLSERVER:\> $b|gm
TypeName: System.Data.DataRow
Name MemberType Definition
---- ---------- ----------
AcceptChanges Method void AcceptChanges()
......
ToString Method string ToString()
Item ParameterizedProperty System.Object Item(int columnIndex) {get;set;}, System.Object Item(string co...
a Property int a {get;set;}
Most of the time in PowerShell, functions/cmdlets that return objects, simply return the object. If more than 1 object is to be returned, the function just keeps returning objects until it's done. PowerShell handles all of the returned objects and gives you an array.
gm is an alias for Get-Member. When you call it by piping to it $a | gm, you are invoking it as a pipeline. In this case, each object in $a is individually passed to Get-Member, so it returns the type of the individual object(s). If they are all the same, then it will only display it once, but it's actually checking all of them. You can prevent this by calling Get-Member -InputObject $a which should show you the array type if it is an array.
Similar to the above, .GetType() gets the type of whatever object it's invoked on, so if it's an array, then it returns that; it's not looking at the individual elements.
I also want to point out that % (ForEach-Object) and foreach() work fine when not used on an array: "hello" | % { $_ }, as does foreach($msg in "hello") { $msg }.
To address the issue of $a.Length not working when the return value is a single object, it depends on your powershell version.
In version 2, you will see the behavior you are seeing: you'll have to wrap it in an array first, or test for an array with something like:
if ($a -is [Array]) {
$itemCount = $a.Length
} else {
$itemCount = 1
}
But, in powershell 3+, you can do $a.Length even if $a is just some object and not an array. It will return 1. Length may not show up in autocomplete or in intellisense, but it will work.
I want to store just the value of a PowerShell object in a variable.
Example
If we run:
$time = Get-Process System | select TotalProcessorTime
$time
This is the current output:
TotalProcessorTime
------------------
00:03:22.8281250
This is the output I would like:
00:03:22.8281250
Discussion
How do we store just the value? If we run $time | Get-Member, we see that PowerShell has stored a Selected.System.Diagnostics.Process that has the following properties:
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
TotalProcessorTime NoteProperty System.TimeSpan TotalProcessorTime=00:03:22.8281250
I have tried getting the value by running both $time.TotalProcessorTime and $time.ToString() without success.
The Object which produces that string "00:03:22.8281250":
(Get-Process System).TotalProcessorTime
The string itself:
(Get-Process System).TotalProcessorTime.ToString()