I have a PS object and couldnt figure out a way to append values to my object.
$object = New-Object PSObject
Add-Member -InputObject $object -MemberType NoteProperty -Name Col1 -Value ""
Add-Member -InputObject $object -MemberType NoteProperty -Name Col2 -Value ""
Add-Member -InputObject $object -MemberType NoteProperty -Name Type -Value ""
1..10 |ForEach{
$src=$_
11..20 | ForEach{
$dst = $_
$object.Col1=$src
$object.Col2=$dst
$object.Type="New"
}
}
I want my result like
col1 col2 Type
---- ---- ----
1 11 New
1 12 New
1 13 New
1 14 New
...
Use a PSCustomObject:
$values = 1..10 | % {
[pscustomobject]#{ col1=1; col2=10+$_; Type="New" }
}
The output you want is a list of objects, not a single object. You generate that by creating the objects inside the loop. #Burt_Harris already showed you one way to do that (using a type accelerator [PSCustomObject]), but of course you can also use New-Object to the same end:
$list = 1..10 | ForEach-Object {
$src = $_
11..20 | ForEach-Object {
$prop = #{
Col1 = $src
Col2 = $_
Type = 'New'
}
New-Object -Type PSObject -Property $prop
}
}
Create the property hashtable as an ordered hashtable if you want the properties to appear in a particular order in the output (PowerShell v3 and newer only):
$prop = [ordered]#{
Col1 = $src
Col2 = $_
Type = 'New'
}
The list of objects can be captured by assigning the pipeline output to a variable ($list = ...).
Related
I have an array of information in Powershell that is comprised of objects within objects. It goes down 4 objects deep. Is there a way to select a property from the bottom most object while remaining at the top level? Does this make sense what I am asking?
$result
active : active
security : #{waf=; acls=}
sealloaction :
siteDualFactorSettings : #{enabled=False; version=0}
login_protect : #{enabled=False; url_patterns=System.Object[]}
performance_configuration : #{advanced_caching_rules=; acceleration_level=standard; cache300x=False; cache_headers=System.Object[]}
$result.security
waf acls
--- ----
#{rules=System.Object[]} #{rules=System.Object[]}
$result.security.waf
rules
-----
{#{action=api.threats.action.block_request; action_text=Block; id=api.threats.sql_injection; name=SQL Injection}, #{action=api.threats.action.alert; action_text=Alert Only;}
To produce a mixed main/nested level selection:
foreach on the main array
select of the nested stuff + calculated properties to reference the iterated main array element
$results | ForEach {
$r = $_
$r.security.waf.rules | select *, #{N='domain'; E={$r.domain}}
}
Or as a one-liner:
$results | %{ $r = $_; $r.security.waf.rules | select *, #{N='domain'; E={$r.domain}} }
$myArray | where { $_.x.y.z -eq 'something' }
which is functionally equivalent to
foreach ($object in $myArray)
{
if ($object.x.y.z -eq 'something')
{
$object # Writes $object to the output stream
}
}
The easier way should be with $result | select ${L='label';E={$_.security.waf}} then you drill down to where you want.
For example:
$obj1_child = New-Object -TypeName psobject
$obj1_child | add-member -Name obj1_child -MemberType NoteProperty -Value "obj1_child"
$obj1_child | add-member -Name value1 -MemberType NoteProperty -Value "child_value1"
$obj1 = New-Object -TypeName psobject
$obj1 | add-member -Name obj1 -MemberType NoteProperty -Value "obj1"
$obj1 | add-member -Name child -MemberType NoteProperty -Value $obj1_child
$obj2 = New-Object -TypeName psobject
$obj2 | add-member -Name value1 -MemberType NoteProperty -Value "value1"
$parentObj = New-Object -TypeName psobject
$parentObj | Add-Member -MemberType NoteProperty -name string -value "parentObject"
$parentObj | Add-Member -MemberType NoteProperty -name OBJ1 -value $obj1
$parentObj | Add-Member -MemberType NoteProperty -name OBJ2 -value $obj2
$parentObj | select string,obj2, #{L='value of obj1 child';E={$_.obj1.child.value1}}
I create a $parentObj inside there's a string, and two objects ($obj1,$obj2), and inside $obj1 there's another object ($obj1_child). Now I want the string from $parentObj and the last string from $obj1_child this should work:
$parentObj | select string, #{L='value of obj1 child';E={$_.obj1.child.value1}}
Output:
>> $parentObj | select string, #{L='value of obj1 child';E={$_.obj1.child.value1}}
string value of obj1 child
------ -------------------
parentObject child_value1
Not sure if it's what you want. but it was what I was looking for when landed here, so may help someone else.
Reference:
https://devblogs.microsoft.com/scripting/access-objects-inside-other-objects-in-powershell-pipeline/
Two ways to do the same thing
$x = [PSCustomObject]#{
active = 'active';
security = [PSCustomObject]#{
waf = [PSCustomObject]#{
rules = #(
[PSCustomObject]#{
action='api.threats.action.block_request';
action_text='Block';
id='api.threats.sql_injection';
name='SQL Injection'},
[PSCustomObject]#{
action='api.threats.action.alert';
action_text='Alert Only';}
)
}
acls = [PSCustomObject]#{
rules = #(
[PSCustomObject]#{
action='api.threats.action.block_request';
action_text='Block';
id='api.threats.sql_injection';
name='SQL Injection'},
[PSCustomObject]#{
action='api.threats.action.alert';
action_text='Alert Only';}
)
}
}
}
$x.security.waf.rules | select *, #{name='domain'; ex={'foo'}}
$x | select -ExpandProperty security | select -ExpandProperty waf | select -ExpandProperty rules | select *, #{name='domain'; ex={'foo'}}
I have a custom PS Object that is something like the below:
ID Folder
MyServer01 \\Server\Share\Share\MyServer01
MyServer02 \\Server\Share\Share\MyServer02
Naturally the object itself is rather large, with over 1000 entries. I need to be able to select a specific row of the object based on querying the ID.
I thought something like this would work but I'm not having much luck:
$obj | Select-Object | Where-Object ($_.ID -eq "MyServer01")
I need it to return the entire row, so the above (assuming it worked) would return:
MyServer01 \\Server\Share\Share\MyServer01
EDIT:
foreach ($mf in $Folders.Tables[0]) {
$Info = New-Object System.Object
$Info | Add-Member -Type NoteProperty -Name ID -Value $mf.ID
$Info | Add-Member -Type NoteProperty -Name Folder -Value $mf.Folder
$obj += $Info
}
Use a hashtable for storing your objects:
$obj = #{}
foreach ($mf in $Folders.Tables[0]) {
$Info = New-Object -Type System.Object
$Info | Add-Member -Type NoteProperty -Name ID -Value $mf.ID
$Info | Add-Member -Type NoteProperty -Name Folder -Value $mf.Folder
$obj[$mf.ID] = $Info
}
Don't append to an array in a loop, as that tends to perform poorly.
If your code doesn't depend on the objects being created explicitly as System.Object I'd also recommend to create them as custom objects:
$obj = #{}
foreach ($mf in $Folders.Tables[0]) {
$Info = New-Object -Type PSCustomObject -Property #{
'ID' = $mf.ID
'Folder' = $mf.Folder
}
$obj[$mf.ID] = $Info
}
Is it possible to create a Custom Object (PSObject) and define its properties beforehand and later in the program execution, we keep adding array of values to the object.
For e.g;
$c = #()
$c = New-Object PSObject
$c | Add-Member -type NoteProperty -name Name
$c | Add-Member -type NoteProperty -name Gender
$c | Add-Member -type NoteProperty -name Age
$c | Add-Member -type NoteProperty -name Name -value "John"
$c | Add-Member -type NoteProperty -name Gender -value "Male"
$c | Add-Member -type NoteProperty -name Age -value "30"
Thanks in advance for any leads or advice.
I'm not sure I follow. Do you want an array of objects with your specified properties? Because your sample first creates an array, that you then overwrite into a single object. So you lost your array.
You can create the object using new-object and specify the properties with values as a hashtable in the -Property parameter. Like this:
$c = New-Object psobject -Property #{
Name = "John"
Gender = "Male"
Age = 30
}
To make an array of them, you can use:
$myarray = #()
$myarray += New-Object psobject -Property #{
Name = "John"
Gender = "Male"
Age = 30
}
If you have multiple tests that you run one by one, you can run the tests in a function that tests and creates a "resultobject", then you collect it:
$myresults = #()
function mytests($computer) {
#Test connection
$online = Test-Connection $computer
#Get buildnumber
$build = (Get-WmiObject win32_operatingsystem -ComputerName $computer).buildnumber
#other tests
#output results
New-Object psobject -Property #{
Online = $online
WinBuild = $build
}
}
$myresults += mytests -computer "mycomputername"
Yeah, so I know this is an old post but Don Jones did something like this:
$props = #{
Name = "John"
Gender = "Male"
Age = 30
}
$c = New-Object PSObject -Property $props
You can run the following to see the Properties and Values the new Object:
c$ | Get-Member
I think that's what you're looking for.
I like PSList, and use the CPU and Elapsed times to indentify processes that need to be killed. I would like to wrap it in a powershell function that returns pipeline values so that I could do something like the following:
get-remoteprocess server1 | where {$_.CPUMinutes -gt 4}
I get stuck in returning values from the function - which I understand would need to be an array of objects.
Heres what my first version looks like:
function get-remoteproc {
param ($hostname)
$results = pslist "\\$hostname"
for ($i= 3; $i -le $results.length; $i++) {
$strline = $results[$i]
$StrWithPrefix = " "+$results[$i]
$trimmedline = $StrWithPrefix -replace '\s+', " ";
$Splitline = $trimmedline.Split(" ")
$ProcessName = $Splitline[1]
.
.
$ProcessCPUTime = ":"+[string]$Splitline[7]
$SplitCpuTime = $ProcessCPUTime.Split(":")
$CpuHours = $SplitCpuTime[1]
$CpuMinutes = $SplitCpuTime[2]
$CpuSeconds = $SplitCpuTime[3]
.
.
.
$obj = New-Object PSObject
$obj | Add-Member Noteproperty -Name "Name" -Value $Name
$obj | Add-Member Noteproperty -Name "PPid" -Value $Ppid
$obj | Add-Member Noteproperty -Name "Pri" -Value $Pri
}
Taking Doug Finke's suggestion, which is pleasingly terse, here is my slightly adapted version, which works well with pipeline values.
function get-remoteproc {
param ($hostname=$env:COMPUTERNAME)
$results = PsList "\\$hostname"
foreach($record in $results[3..($results.Count)]) {
# Remove spaces
while ($record.IndexOf(" ") -ge 0) {
$record = $record -replace " "," "
}
$Name,$Processid,$Pri,$Thd,$Hnd,$Priv,$CPUTime,$ElapsedTime = $record.Split(" ")
$properties = #{
Name = $Name
Pid = $Processid
Pri = $Pri
Thd = $Thd
Hnd = $Hnd
Priv = $Priv
CPUTime = $CPUTime
ElapsedTime = $ElapsedTime
}
New-Object PSObject -Property $properties |
Add-Member -PassThru ScriptProperty CPUH {[int]$this.CPUTime.Split(":")[0]} |
Add-Member -PassThru ScriptProperty CPUM {[int]$this.CPUTime.Split(":")[1]} |
Add-Member -PassThru ScriptProperty CPUS {[int]$this.CPUTime.Split(":")[2]} |
Add-Member -PassThru ScriptProperty ElaH {[int]$this.ElapsedTime.Split(":")[0]} |
Add-Member -PassThru ScriptProperty ElaM {[int]$this.ElapsedTime.Split(":")[1]} |
Add-Member -PassThru ScriptProperty ElaS {[int]$this.ElapsedTime.Split(":")[2]}
}
}
And a call to the function, which shows that the objects are unrolled correctly for consumption by the pipeline:
get-remoteproc "Server1" | where {(($_.CPUM * $_.CPUS) -gt 60) -and ($_.name -notmatch "Idle" )}|
ft name, pid, pri, thd, hnd, priv, CPUH, cpuM, cpuS, ElaH, ElaM, ElaS -auto
Thanks, everyone!
As empo points out, you need to emit the $obj back to the pipeline. Here is another way to work the plist text into PowerShell objects.
function get-remoteproc {
param ($hostname=$env:COMPUTERNAME)
$results = PsList "\\$hostname"
foreach($record in $results[3..($results.Count)]) {
# Remove spaces
while ($record.IndexOf(" ") -ge 0) {
$record = $record -replace " "," "
}
$Name,$Processid,$Pri,$Thd,$Hnd,$Priv,$CPUTime,$ElapsedTime = $record.Split(" ")
$properties = #{
Name = $Name
Pid = $Processid
Pri = $Pri
Thd = $Thd
Hnd = $Hnd
Priv = $Priv
CPUTime = $CPUTime
ElapsedTime = $ElapsedTime
}
New-Object PSObject -Property $properties |
Add-Member -PassThru ScriptProperty CPUHours {$this.CPUTime.Split(":")[0]} |
Add-Member -PassThru ScriptProperty CPUMinutes {$this.CPUTime.Split(":")[1]} |
Add-Member -PassThru ScriptProperty CPUSeconds {$this.CPUTime.Split(":")[2]}
}
}
To return an array of values passed to the pipeline one by one, you just need to return multiple values in your function. For example:
function get-remoteproc {
param ($hostname)
$results = pslist "\\$hostname"
for ($i= 3; $i -le $results.length; $i++) {
# your staff to $obj
# output $obj
$obj
}
}
The function result is an array of $obj. When connected to a pipeline the array will be enrolled, and all the values will be passed to the stream one by one.
I am creating a new object in a Powershell script, or actually an object type. I want to create multiple instances of this object. How do I do this?
The code below is what I am working on, it appears that all instances in the array reference the same object, containing the same values.
# Define output object
$projectType = new-object System.Object
$projectType | add-member -membertype noteproperty -value "" -name Project
$projectType | add-member -membertype noteproperty -value "" -name Category
$projectType | add-member -membertype noteproperty -value "" -name Description
# Import data
$data = import-csv $input -erroraction stop
# Create a generic collection object
$projects = #()
# Parse data
foreach ($line in $data) {
$project = $projectType
$project.Project = $line.Id
$project.Category = $line.Naam
$project.Description = $line.Omschrijving
$projects += $project
}
$projects | Export-Csv output.csv -NoTypeInformation -Force
You have to use New-Object for any, well, new object, otherwise being a reference type $projectType in your code refers to the same object. Here is the changed code:
# Define output object
function New-Project {
New-Object PSObject -Property #{
Project = ''
Category = ''
Description = ''
}
}
# Parse data
$projects = #()
foreach ($line in 1..9) {
$project = New-Project
$project.Project = $line
$project.Category = $line
$project.Description = $line
$projects += $project
}
# Continue
$projects
In this particular case instead of using the function New-Project you can just move its body into the loop, e.g. $project = New-Object PSObject …. But if you create your “projects” elsewhere then having this function will be useful there as well.