Sample input:
Hostnames
Status
Time
Host1
Retired
1/31/2023 9:27:06 PM
Host2
Active
2/1/2023 11:45:48 AM
Host1
Active
2/10/2023 4:59:27 AM
Host1
Active
2/13/2023 1:24:50 PM
Host2
Retired
2/10/2023 1:17:29 PM
Now, from above table, I would like to get rows of each hostname with latest timestemp.
In above example, it should return below rows as output:
Hostnames
Status
Time
Host1
Active
2/13/2023 1:24:50 PM
Host2
Retired
2/10/2023 1:17:29 PM
Any suggestions on how this can be achieved?
For testing I imported the data from CSV and then added a column with DateTime so I could sort. See following :
$data = #'
"Hostnames","Status","Time"
"Host1","Retired","1/31/2023 9:27:06 PM"
"Host2","Active","2/1/2023 11:45:48 AM"
"Host1","Active","2/10/2023 4:59:27 AM"
"Host1","Active","2/13/2023 1:24:50 PM"
"Host2","Retired","2/10/2023 1:17:29 PM"
'#
$table = $data | ConvertFrom-Csv
foreach($row in $table)
{
$row | Add-Member -NotePropertyName DateTime -NotePropertyValue ([DateTime]$row.Time)
}
$table = $table | Sort-Object -Property DateTime -Descending
$groups = $table | Group-Object -Property HostNames
$results = $groups | foreach { $_.Group[0] }
$results
Assuming you have that data imported in a variable $inputData, the easiest way is to group that on property Hostnames first and then sort each group on the Time value converted to real DateTime object.
Something like this
$newData = $inputData | Group-Object Hostnames | ForEach-Object {
$_.Group | Sort-Object {[datetime]$_.Time} | Select-Object -Last 1
}
# display on screen
$newData | Format-Table -AutoSize
# write to csv file
$newData | Export-Csv -Path 'X:\Somewhere\LatestStatus.csv' -NoTypeInformation
Output when displayed on screen:
Hostnames Status Time
--------- ------ ----
Host1 Active 2/13/2023 1:24:50 PM
Host2 Retired 2/10/2023 1:17:29 PM
Related
What is the simplest way to write this in PowerShell:
SELECT col1, SUM(col3) AS SumOfValues
FROM dbo.Table
GROUP BY col1
How can I combine Group-Object and Measure-Object?
I have found this answer link, but there must be a simpler way.
Like this? With a calculated property at the end.
'id,amount
1,4
1,5
2,4
2,6
3,3
3,5' | convertfrom-csv | group id | select name,
#{n='Sum';e={$_.group.amount | measure -sum | % sum}}
Name Sum
---- ---
1 9
2 10
3 8
You need to use the property GROUP. The script outputs "$group | Format-Table" which shows what the real issue is.
This is what each row of the Group Looks like :
Count Name Group
----- ---- -----
3 a {#{col1=a; col2=x; col3=1}, #{col1=a; col2=x; col3=2}, #{col1=a; col2=x; col3=3}}
See code below :
$input = #"
col1,col2,col3
a,x,1
a,x,2
a,x,3
b,x,4
b,x,5
b,x,6
c,x,7
c,x,8
c,x,9
"#
$table = $input | ConvertFrom-Csv
$table | Format-Table
$groups = $table | Group-Object {$_.col1}
$outputTable = [System.Collections.ArrayList]::new()
foreach($group in $groups)
{
$group | Format-Table
$newRow = New-Object -TypeName psobject
$newRow | Add-Member -NotePropertyName col1 -NotePropertyValue $group.Name
$sum = 0
$group.Group | foreach{$sum += $_.col3}
Write-Host "sum = " $sum
$newRow | Add-Member -NotePropertyName SumOfValues -NotePropertyValue $sum
$outputTable.Add($newRow) | Out-Null
}
$outputTable | Format-Table
I want to get an output like below.
My output now :
Name Active PrimarySmtpAddress
---- ------ ------------------
DG_Group1 True mail1#contoso.com
DG_Group2 False mail2#contoso.com
DG_Group3 True mail3#contoso.com
My desired output :
mail1#contoso.com
mail2#contoso.com
mail3#contoso.com
script :
$DistroLists = Get-DistributionGroup -ResultSize Unlimited
$MessageTrace = Get-MessageTrace -RecipientAddress $DistroLists.PrimarySmtpAddress -startdate (Get-Date).AddDays(-8) -EndDate (Get-Date)
$DistroLists |
Foreach-Object {
$_ | Add-Member -MemberType NoteProperty -Name Active -Value (
$_.PrimarySmtpAddress -in $MessageTrace.RecipientAddress
) -PassThru
} |
Select-Object Name, Active, PrimarySmtpAddress | Where-Object Active -EQ "FALSE"
Out-File C:\output.txt
You can use Select-Object -ExpandProperty or ForEach-Object -MemberName to grab only the value of a specific property from one or more piped input objects:
... |Select-Object Name, Active, PrimarySmtpAddress | Where-Object Active -eq $false | ForEach-Object -MemberName PrimarySmtpAddress | Out-File...
You can skip first two lines and split the line using space characters. Finally pick the last column as given below:
Get-Content C:\Projects\logtext.txt | Select-Object -Skip 2 | ForEach-Object { "$(($_
-split '\s+',3)[2])" }
mail1#contoso.com
mail2#contoso.com
mail3#contoso.com
I am trying to change the format of an arraylist after I have used
group-object
to count all the entries in the list.
this is a sample
$list = [System.Collections.ArrayList]#()
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$list | Group-Object | select name,count
This is the sample output
Name Count
---- -----
letter 1
solo 1
nap 3
sharp 1
ignite 1
tap 4
evoke 1
What I would like is
letter solo nap sharp ignite tap evoke
-------- ----- ---- ---- ----- ------ ----
1 1 3 4 1 4 1
Then when exporting to excel it would format like this
Everything I have tried doesn't seem to pay off, or even get close to what I am trying to do and I think I am missing something obvious or have hit my PowerShell skill limitations. Could someone please help. Thank you
You could create a PSObject, add the properties to it with Add-Member, then format the output to a table with Format-Table:
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$groups = $list | Group-Object | Select-Object Name, Count
$psObject = New-Object -TypeName psobject
foreach ($group in $groups) {
$psObject | Add-Member -NotePropertyName $group.Name -NotePropertyValue $group.Count
}
$psObject | Format-Table
Output:
evoke ignite letter nap sharp solo tap
----- ------ ------ --- ----- ---- ---
1 1 1 3 1 1 4
Skip Group-Object altogether - instead, use a dictionary to keep track of the count, then cast the whole dictionary to a custom object:
$properties = [ordered]#{}
$list |ForEach-Object {
$properties[$_]++
}
$counts = [pscustomobject]$properties
$counts will now hold an object like what you describe, formatting as a table gives you:
PS C:\> $counts |Format-Table
letter solo nap sharp ignite tap evoke
------ ---- --- ----- ------ --- -----
1 1 3 1 1 4 1
You may try something like:
$list = [System.Collections.ArrayList]#()
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$group = $list | Group-Object | select name,count
$a = [PSCustomObject]#{}
foreach ($item in $group) {
$a | Add-Member -NotePropertyName $item.name -NotePropertyValue $item.count
}
$a | ft
One solution would be to put it into an PsObject and then export that object into a CSV:
$list = [System.Collections.ArrayList]#()
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$hash = $list | Group-Object | select name,count
$object = New-Object psobject
foreach( $item in $hash ) {
$column_name = $item.Name
$row_value = $item.Count
$object | Add-Member -MemberType NoteProperty -Name $column_name -Value $row_value
}
$object | Export-csv 'Path to your CSV' -NoTypeInformation
I need to decide the columns' orders of my table. My actual command is that one:
$tab | Sort-Object "Pourcentage" -Descending |
Format-Table -AutoSize |
Out-String -Width 4000 |
Out-File -Append O:\sigdci\public\parcoursArborescence\bilanAnalyse.txt
It gives me that order:
Derniere modification Categorie recherchee Dernier acces Dossier Pourcentage
But I need "Dossier" to be first, then "Categorie recherchee" and "Pourcentage" shall be 2nd and 3rd. How shall I proceed?
Specify the column headers in the desired order:
$tab | Sort-Object "Pourcentage" -Descending |
Format-Table 'Dossier', 'Categorie recherchee', 'Pourcentage',
'Derniere modification', 'Dernier acces' -AutoSize |
Out-String -Width 4000 |
Out-File -Append 'O:\sigdci\public\parcoursArborescence\bilanAnalyse.txt'
If you need to dynamically determine the column names you could do it like this:
$headers = $tab[0].PSObject.Properties |
Where-Object MemberType -eq NoteProperty |
Select-Object -Expand Name
However, you'd have to bring that list into your desired order somehow. Perhaps you could do it like this:
$allHeaders = 'Dossier', 'Categorie recherchee', 'Pourcentage',
'Derniere modification', 'Dernier acces'
$actualHeaders = $tab[0].PSObject.Properties |
Where-Object { MemberType -eq NoteProperty } |
Select-Object -Expand Name
$headers = $allHeaders | Where-Object { $actualHeaders -contains $_ }
$allHeaders is an array that contains all headers in the correct order. Then you remove all items that aren't present in $actualHeaders from that list, preserving the order of the remaining headers.
I have a CSV file, Devices.csv, containing IP Address, DeviceName, SerialNumber, MAC Address, UserName.
The Users column in all the rows of Devices.csv is prepopulated with a value [Unknown]
The code...
{Import-CSV Devices.csv | Where-Object {$_.IPAddress -eq '192.168.2.124'} | Select-Object -last 1 | FT IPAddress, devicveName, SerialNumber, MACAddress, User -AutoSize }
....outputs
IPAddress deviceName SNumber MACAddress User
--------- ----- ------- ---------- ----
192.168.2.124 ComputerA 1ABCDEFG 00xxYYbbCCdd [Unknown]
I want to be able to replace the [Unknown] text with a Username I have retrieved from a different source. Can I update just the User column on this line in Devices.csv using powershell and keep the rest of the CSV file intact ?
Thanks, Brian
$csv = Import-CSV Devices.csv
$csv | Where-Object {$_.IPAddress -eq '192.168.2.124'} | Select-Object -Last 1 | ForEach-Object {$_.User = $user}
$csv | Export-Csv Devices.csv -NoTypeInformation
Try this:
$otheruser = '...'
Import-Csv Devides.csv | % {
if ($_.User -eq '[Unknown]') { $_.User = $otheruser }
$_
} | Export-Csv Divices_modified.csv -NoTypeInformation