Jump to content

CSV doppelte Zeilen löschen


Recommended Posts

Hallo werte Forengemeinde,

 

ich beisse mich seit einigen Tagen an einem Problem fest, was wohl laut diverser Foren viele haben, dafür gab es auch schon haufenweise Lösungsvorschläge, aber nichts davon führte bei mir zum Erfolg. Es geht darum dass ich eine Inventarliste im CSV Format (kommagetrennt) habe, die sieht folgendermassen aus (Spaltenanzahl und Zeilenanzahl aus Komplexitätsgründen gekürzt)

 

Zitat

Hostname    Timestamp             Serial Number       Manufacturer          Model       
RTA190504    11.11.2021 14:25    S4JY3833         LENOVO            30C5003CGE       
RTA200601    11.11.2021 12:23    CZC02286W9        HP                HP ProDesk 600
RTA201201    27.09.2021 08:39    33M6933            Dell Inc.         OptiPlex 3070    
RTX54011      15.07.2021 18:19    7739-6127        Microsoft        Virtual Machine    
NTX210708    12.11.2021 21:02    PF2WCBGR        LENOVO            82GN    
NTX210708    13.11.2021 11:31    PF2WCBGR        LENOVO            82GN    
NTX210708    13.11.2021 11:32    PF2WCBGR        LENOVO           82GN    
NTX210708    13.11.2021 11:30    PF2WCBGR        LENOVO            82GN    
NTX210708    13.11.2021 11:31    PF2WCBGR        LENOVO            82GN    
NTX210708    13.11.2021 11:33    PF2WCBGR        LENOVO            82GN   

Wie hier deutlich zu sehen sind von NTX210708 mehere Einträge vorhanden. Ist es möglich per Batch oder auch per PS alle doppelten Zeilen zu löschen, Dabei sollen die Zeilen behalten werden die dem aktuellstzen Timestamp entsprechen. In dem Fall von NTX... also der letzte.

 

Vielleicht hat ja einer eine Idee?

 

 

lg und schönes we

 

 

Stef

Link to post

Hi,

 

was hast du denn so an Lösungsvorschlägen bereits umgesetzt? Generell ist das mit "Import-CSV (Import-Csv (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Docs)" und ein bisschen Logik flott erledigt. In quick'n'dirty:

$CSVPath = "<Pfad zur CSV>"
$objCSVInput = Import-Csv -Path $CSVPath `
    -Delimiter ","
$objHostnames = $objCSVInput |
    Select-Object Hostname -Unique

$objCSVSorted = @()
foreach($Hostname in $objHostnames.Hostname){
    $objCSVSorted += $objCSVInput |
        Where-Object { $_.Hostname -eq $Hostname } |
            Sort-Object Timestamp |
                Select-Object -Last 1
}

$objCSVSorted

 

Gruß

Jan

Edited by testperson
Link to post

@testperson Das würde funktionieren, wenn Import-CSV das Timestamp-Feld als [datetime] importieren würde. Da es aber ein String bleibt, ist '01.01.2021 12:30' < '12.12.1999 13:45'

Mein Vorschlag wäre

$data = Import-CSV C:\temp\machines.csv
$lastTS = @{}
$output = @{}
for ($i=0; $i -lt $data.Count; $i++) {
    $curHostname = $data[$i].Hostname
    $curTS = $data[$i].Timestamp -as [datetime]
    if ($lastTS.ContainsKey($curHostname)) {
        if ($lastTS[$curHostname] -lt $curTS) {
            $lastTS[$curHostname] = $curTS
            $output[$curHostname] = $i
        }
    } else {
        $lastTS.Add($curHostname, $curTS)
        $output.Add($curHostname, $i)
    }
}
$outputSorted = New-Object System.Collections.Generic.List[PSObject]
foreach($i in $output.Values) {
    $outputSorted.Add($data[$i])
}
$outputSorted | Export-CSV -Path c:\temp\machines-sorted.csv -Delimiter ',' -NoTypeInformation

Vermutlich ließe er sich bei sehr großen Datenmengen nochmal optimieren, aber er dürfte schneller sein als alles, was mit der Pipe und Sort-Object arbeitet.

Edited by cj_berlin
Link to post

@cj_berlin Das sollte sich doch in meinem Code so lösen lasse:

$CSVPath = "C:\install\test\test.csv"
$objCSVInput = Import-Csv -Path $CSVPath `
    -Delimiter ","
$objHostnames = $objCSVInput |
    Select-Object Hostname -Unique

$objCSVSorted = @()
foreach($Hostname in $objHostnames.Hostname){
    $objCSVSorted += $objCSVInput |
        Where-Object { $_.Hostname -eq $Hostname } |
            Sort-Object [datetime]::Timestamp |
                Select-Object -Last 1
}

$objCSVSorted

 

Link to post

Funktional schon :-) Und es erschließt sich aus Deinem Code für den Laien vermutlich auch besser, was der Code wo tut.

 

Aber: ich habe hier eine Datei mit 100k Zeilen für insgesamt 1000 Hostnames, also quasi real-world data aus einem mittelständischen Unternehmen. Mein obiger Code war damit in unter zwei Sekunden fertig. Dahingegen hat auf demselben Rechner allein der Part mit Select -Unique 30-40 Sekunden in Anspruch genommen.

 

Hier mit Hard Facts:

image.png.b96a15497f072bf6e329657e60550190.png

Edited by cj_berlin
  • Like 1
Link to post

Also ich hatte es folgendermaaßen probiert, nachdem ich mit Batch absolut nicht weiterkam:

 

Zitat

#Getting the Path of CSV file
$inputCSVPath = 'Summary.csv'
#The Import-Csv cmdlet creates table-like custom objects from the items in CSV files
$inputCsv = Import-Csv $inputCSVPath | Sort-Object Timestamp -Unique
#The Export-CSV cmdlet creates a CSV file of the objects that you submit.
#Each object is a row that includes a comma-separated list of the object's property values.
$inputCsv | Export-Csv "C:\Users\Riegert\Desktop\CSVmerge\Final.csv"  -NoTypeInformation

Die Reihenfolge ist im Prinzip ertmal egal, klar wärs schön wenns nach Host sortiert wäre. Danke aber nochmal für die interessanten Lösungsansätze, werde ich mich mal durcharbeiten, melde mich dann wieder

 

 

lg

 

Stef

 

 

Also folgendes:

 

Also prinipiell scheint er bei allen Lösungen etwas zu machen, also auch keine doppelten, aber es schreibt leider keine Ergennis CSV. Schon komisch dass ich in anderen Foren zu dem Thema nur "Müll" gefunden habe.

 

@CJ Wie bekomme ich es bei Deinem Ansatz hin dass er eine neue Datei schreibt, so im Fenster kann ich damit nicht viel anfangen, bin leioder noch nicht ganz so firm in Powershell, obgleich ich zugeben muss, dass man damit schon mehr anfangen kann als mit Batch.

 

lg

 

Stef

 

 

 

Link to post

Moin,

 

in beiden Varianten machst Du am Ende | Export-CSV -Path <Pfad> -Delimiter ',' -NoTypeInformation :-)

Aber ich ergänze mal den obigen Code um eine Sammlung.

 

EDIT: Code inklusive CSV-Ausgabe ist drin. Wenn Du möchtest, dass die CSV in Excel mit Doppelklick aufgeht, musst Du den Delimiter auf ';' setzen.

Edited by cj_berlin
Link to post

Ich bin mir nicht sicher, ob ich dich richtig verstanden habe, du meintest sicher jeweils in den Zeilen mit Output:

 

Zitat

$data = Import-CSV C:\SC\CSVmerge\Summary.csv
$lastTS = @{}
$output = @{}
for ($i=0; $i -lt $data.Count; $i++) {
    $curHostname = $data[$i].Hostname
    $curTS = $data[$i].Timestamp -as [datetime]
    if ($lastTS.ContainsKey($curHostname)) {
        if ($lastTS[$curHostname] -lt $curTS) {
            $lastTS[$curHostname] = $curTS
            $output[$curHostname] = $i | Export-CSV -Path C:\SC\CSVmerge\output.csv -Delimiter ',' -NoTypeInformation
        }
    } else {
        $lastTS.Add($curHostname, $curTS)
        $output.Add($curHostname, $i) | Export-CSV -Path C:\SC\CSVmerge\output.csv -Delimiter ',' -NoTypeInformation
    }
}
foreach($i in $output.Values) {
    $data[$i]
}

die Outputdatei ist 1kb groß und zeigt beim öffnen keinen Inhalt, vielleicht fehlen mir ja noch irgendwelche cmdlets, die ich erst isstallieren muss

Link to post

da steht nur

EDIT: Code inklusive CSV-Ausgabe ist drin. Wenn Du möchtest, dass die CSV in Excel mit Doppelklick aufgeht, musst Du den Delimiter auf ';' setzen.

bearbeitet vor 48 Minuten von cj_berlin  aber sehe leider nix:(

 

achso da oben, jetzt seh ichs, ich probiers

Edited by stefan4711
Link to post

Also jetzt hab ichs so, der Pfad zu Summary.csv stimmt, aber jetzt macht er gar nichts

 

data = Import-CSV C:\SC\CSVmerge\Summary.csv
$lastTS = @{}
$output = @{}
for ($i=0; $i -lt $data.Count; $i++) {
    $curHostname = $data[$i].Hostname
    $curTS = $data[$i].Timestamp -as [datetime]
    if ($lastTS.ContainsKey($curHostname)) {
        if ($lastTS[$curHostname] -lt $curTS) {
            $lastTS[$curHostname] = $curTS
            $output[$curHostname] = $i
        }
    } else {
        $lastTS.Add($curHostname, $curTS)
        $output.Add($curHostname, $i)
    }
}
$outputSorted = New-Object System.Collections.Generic.List[PSObject]
foreach($i in $output.Values) {
    $outputSorted.Add($data[$i])
}
$outputSorted | Export-CSV -Path C:\SC\CSVmerge\machines-sorted.csv -Delimiter ',' -NoTypeInformation

:(

 

lg

 

stef

Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...