CSV Datei in PS bearbeiten und wieder speichern
Hallo Liebe Administrator Gemeinde.
Ich habe folgendes Anliegen, ich habe eine immer wieder kehrende Aufgabe welche ich gern automatisieren möchte.
Es gibt eine Date welche zum Beispiel "Pfusch1.csv" heißt und im Ordner C:\ErstePSErfahrung\ liegt.
Die csv Datei hat ein ; als trennzeichen und ist mit folgendem Inhalt versehen.
Spalte1;Spalte2;;;Spalte5;Spalte6;
Spalte1;Spalte2;;;Spalte5;Spalte6;
daraus möchte ich nun den Inhalt der Spalte2 löschen (aber nicht die ganze Spalte).
Spalte1;;;;Spalte5;Spalte6;
Spalte1;;;;Spalte5;Spalte6;
Im Anschluss soll die Datei wieder im Ordner C:\ErstePSErfahrung\ gespeichert werden.
Ich könnte mir das unter Linux durch einen Import, den sed Befehl und anschließendem Export schreiben.
Von Powershell haber ich bisher noch keine Erfahrungen sammeln können und hoffe auf Eure Unterstützung.
Beginnen würde ich das Skript mit
Ich habe folgendes Anliegen, ich habe eine immer wieder kehrende Aufgabe welche ich gern automatisieren möchte.
Es gibt eine Date welche zum Beispiel "Pfusch1.csv" heißt und im Ordner C:\ErstePSErfahrung\ liegt.
Die csv Datei hat ein ; als trennzeichen und ist mit folgendem Inhalt versehen.
Spalte1;Spalte2;;;Spalte5;Spalte6;
Spalte1;Spalte2;;;Spalte5;Spalte6;
daraus möchte ich nun den Inhalt der Spalte2 löschen (aber nicht die ganze Spalte).
Spalte1;;;;Spalte5;Spalte6;
Spalte1;;;;Spalte5;Spalte6;
Im Anschluss soll die Datei wieder im Ordner C:\ErstePSErfahrung\ gespeichert werden.
Ich könnte mir das unter Linux durch einen Import, den sed Befehl und anschließendem Export schreiben.
sed -e 's,^\([^;]*\);[^;]*;\(.*\),\1;;\2,'
Von Powershell haber ich bisher noch keine Erfahrungen sammeln können und hoffe auf Eure Unterstützung.
Beginnen würde ich das Skript mit
Get-Content C:\ErstePSErfahrung\Pfusch1.csv
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 665779
Url: https://administrator.de/contentid/665779
Ausgedruckt am: 05.11.2024 um 06:11 Uhr
28 Kommentare
Neuester Kommentar
Moin,
besser direkt mit Import-CSV, so kannst du die Spalten auch direkt ansteuern.
Ein bisschen was zum lesen:
Gruß Thomas
besser direkt mit Import-CSV, so kannst du die Spalten auch direkt ansteuern.
$CSV = Import-CSV -Delimiter ';' -Path 'C:\ErstePSErfahrung\Pfusch1.csv' -Header (1..6)
Foreach ($Line in $CSV) { $Line.2 = '' }
$CSV | Export-Csv -Delimiter ';' -NoTypeInformation 'C:\ErstePSErfahrung\Pfusch1_neu.csv'
Von Powershell habe ich bisher noch keine Erfahrungen sammeln können und hoffe auf Eure Unterstützung.
Ein bisschen was zum lesen:
Zitat von @colinardo:
Powershell Leitfaden für Anfänger
Pflichtlektüre
- Kapitel 1: Erste Schritte mit PowerShell
- Kapitel 2: Das Hilfesystem
- Kapitel 3: Entdecken von Objekten, Eigenschaften und Methoden
- Kapitel 4: Einzeiler und die Pipeline
- Kapitel 5: Formatierung, Aliase, Anbieter, Vergleich
- Kapitel 6: Flusssteuerung
- Kapitel 7: Arbeiten mit WMI
- Kapitel 8: PowerShell-Remoting
- Kapitel 9: Funktionen
- Kapitel 10: Skriptmodule
- Anhang A: Hilfesyntax
Erweiterte Grundlagen
- Alles, was Sie schon immer über Arrays wissen wollten
- Alles, was Sie schon immer über Hashtabellen wissen wollten
- Was Sie schon immer über PSCustomObject wissen wollten
- Alles, was Sie schon immer über die Variablenersetzung in Zeichenfolgen wissen wollten
- Alles, was Sie schon immer über die IF-Anweisung wissen wollten
- Alles, was Sie schon immer über die switch-Anweisung wissen wollten
- Alles, was Sie schon immer über Ausnahmen wissen wollten
- Alles, was Sie schon immer über $null wissen wollten
- Alles, was Sie schon immer über ShouldProcess wissen wollten
- Verwenden von Erweiterung mit der TAB-TASTE
Zusätzliche Ressourcen
Gruß Thomas
Wie genau hast du das Skript ausgeführt?
2) Von der Datei eine Verknüpfung machen (Rechtsklick > kopieren und dann Rechtsklick > Verknüpfung einfügen)
3) In den Verknüpfungseigenschaften sollte bei "Ziel" bereits der Pfad zum ps1-Skript stehen. Vor diesem musst du noch powershell -EP ByPass -File ergänzen. Vollständig sollte die Zeile dann so aussehen:
Falls das Konsolenfenster auch nach Beenden des Skripts geöffnet bleiben soll, ergänzt man zusätzlich noch -NoExit
Ausgeführt wird dann immer die Verknüpfung
Wenn man die ExcecutionPolicy gesetzt hat, kann man bei der Verknüpfung das -EP ByPass weglassen.
Geht am einfachsten so:
1) Datei als .ps1-Datei abspeichern.2) Von der Datei eine Verknüpfung machen (Rechtsklick > kopieren und dann Rechtsklick > Verknüpfung einfügen)
3) In den Verknüpfungseigenschaften sollte bei "Ziel" bereits der Pfad zum ps1-Skript stehen. Vor diesem musst du noch powershell -EP ByPass -File ergänzen. Vollständig sollte die Zeile dann so aussehen:
powershell -EP ByPass -File "C:\Pfad\zum\Skript.ps1"
powershell -EP ByPass -NoExit -File "C:\Pfad\zum\Skript.ps1"
Zitat von @Hacktrist:
Das ist aber nicht das Skript, welches ich gepostet habe.In Zeile:1 Zeichen:46
+ ... -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & 'C ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Die ExcecutionPolicy setzt man einmalig über folgenden Befehl...
... entweder global für alle Benutzer (Powershell muss mit Administratorrechten gestartet werden):
Set-ExecutionPolicy RemoteSigned -Force
... oder nur für den aktuell angemeldeten Benutzer:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned -Force
Wenn man die ExcecutionPolicy gesetzt hat, kann man bei der Verknüpfung das -EP ByPass weglassen.
Richtig, hätte ich auch selbst drauf kommen können,sorry:
$CSV | ConvertTo-Csv -NoTypeInformation -Delimiter ';' | Select -Skip 1 | Set-Content 'C:\ErstePSErfahrung\Pfusch1_neu.csv'
Vielleicht beschreibst Du mir noch kurz die genauen Dinge welche in Zeile 3 und 4 bei den Befehlen genau getan werden.
Der Befehl oben soll Zeile 3 eigentlich ersetzen - es gibt also keine Zeile 4.Vielleicht fangen wir mal besser ganz vorne an...
Warum Import-CSV statt Get-Content?
Get-Content liest eine Datei als Text-Datei ein. Import-CSV hingegen wandelt die CSV-Datei in ein Objekt um, mit den Spalten als Eigenschaften.Zum besseren Verständnis, die einzelnen Befehlszeilen einfach mal in Powershell ausführen und sich den Inhalt der Variable anzeigen lassen:
$CSV = Import-CSV -Delimiter ';' -Path 'C:\ErstePSErfahrung\Pfusch1.csv' -Header (1..6)
$CSV
1 2 3 4 5 6
- - - - - -
Spalte1 Spalte2 Spalte5 Spalte6
Spalte1 Spalte2 Spalte5 Spalte6
Möchte man explizit eine bestimmte Anzeige, kann man das Objekt einfach in Format-Table bzw. Format-List pipen.
$CSV | Format-Table
Der Vorteil eines Objekt, liegt darin, das wir nun die Zeilen, Spalten und Zellen unserer Tabelle einzeln ansprechen können:
$CSV # Gibt die Zeile mit dem Index 0 aus ( Index = Zeilennummer -1 )
$CSV.5 # Gibt die Spalte mit dem Header 5 aus
$CSV[1].6 # Gibt von Zeile 2 die 6. Spalte aus (in Excel also Zelle "F2")
Zeile 2
Foreach ($Line in $CSV) { $Line.2 = '' }
Nun zu deinem Problem von eben:
Ich habe schon versucht das wegzulassen, was jedoch nicht funktioniert. Da läuft es auf Fehler und die Datei ist leer.
Wenn du den Header-Parameter bei Import-CSV weglässt, geht Powershell davon aus, dass in Zeile 1 deiner CSV-Datei Spaltenüberschriften stehen - und nimmt diese dann als Header.-Header (1..6)
Wenn deine CSV-Datei also z.B. so aussähe:
Vorname;Nachname;Telefon
Otto;Müller;12345
Hans;Dieter;67890
$CSV.Nachname
In deinem Fall führt das zu einem Fehler, da jede Spalte einen eindeutigen Namen haben muss. Zudem können wir dann natürlich nicht mehr die Spalte mit dem Namen "2" aufrufen, da es diese nicht gibt.
Die Ausgabe...
Export-CSV Wandelt unser Objekt wieder in eine Textdatei in CSV-Formatierung um; und speichert diese ab - allerdings immer mit Überschriften.ConvertTo-CSV wandelt das Objekt genauso um, jedoch ohne es abzuspeichern. Führe nur folgendes aus:
$CSV | ConvertTo-Csv -NoTypeInformation -Delimiter ';'
Wir könnten die Schritte natürlich auch einzeln ausführen:
$CSV = $CSV | ConvertTo-CSV -nti -d ';' # Objekt in Textdatei konvertieren
$CSV = $CSV | Select-Object -Skip 1 # Erste Zeile überspringen
$CSV | Set-Content 'Neu.CSV' #Datei abspeichern
Zur Vervollständigung:
Prinzipiell könnten wir natürlich auch - wie du es vor hattest - mit Get-Content arbeiten und wie mit sed einen regex-replace machen:$Text = Get-Content 'C:\ErstePSErfahrung\Pfusch1.csv'
$Text = $Text -replace '^([^;]*);[^;]*;(.*)','$1;;$2'
$Text | Set-Content 'C:\ErstePSErfahrung\Neu.CSV'
(Get-Content 'C:\ErstePSErfahrung\Pfusch1.csv') -replace '^([^;]*);[^;]*;(.*)','$1;;$2' | Set-Content 'C:\ErstePSErfahrung\Neu.CSV'
Zitat von @Hacktrist:
Ich habe doch noch 1 Problem. Und zwar wenn ich das Skript über die CSV laufen lasse, sind plötzlich alle Einträge mit "" gekennzeichnet.
Jo, das entspricht allerdings auch dem Dateiaufbau des CSV-Formats, wird bei der Verwendung von Sonderzeichen sogar zwingend erforderlich.Ich habe doch noch 1 Problem. Und zwar wenn ich das Skript über die CSV laufen lasse, sind plötzlich alle Einträge mit "" gekennzeichnet.
Kann man das ändern?
Mit Regex kann man alles 😉($CSV | ConvertTo-CSV -d ';' -nti) -replace '(?<=^|;)"|"(?=;|$)' | Select -Skip 1 | sc 'C:\ErstePSErfahrung\Neu.CSV'
- sc ist ein Alias für Set-Content
- select ist ein Alias für Select-Object
Moin,
das ginge mit Read-Host
anschließend mit Test-Path überprüfen, ob der Ordner existiert.
... oder man nutzt den grafischen Dialog:
Gruß Thomas
das ginge mit Read-Host
$Ordner = Read-Host "Ordnername eingeben"
... oder man nutzt den grafischen Dialog:
# Klasse für grafische Oberflächen laden
Add-Type -AssemblyName System.Windows.Forms
# Öffnendialog erstellen
$Open = New-Object System.Windows.Forms.OpenFileDialog -Property @{
Title = "Öffne CSV-Datei"
Filter = 'CSV-Dateien|*.csv|Alle Dateien|*.*'
}
# Dialog starten
[void]$Open.ShowDialog()
# Ergebnis anzeigen
$Open.FileName
Gruß Thomas
Zitat von @Hacktrist:
O.k.
Und wie übergebe ich den $Ordner in den Import + Datei.csv? (hier Zeile 3)
Ich habs mal einfach mit -Path '$Ordner\datei.csv' versucht. Da crasht er aber.
Mit Single-Quotes übergibt er die Zeichenkette exakt so wie sie ist, also inklusive Dollarzeichen, ohne die Variable aufzulösen. Damit die Variable interpretiert wird, musst du Double-Quotes verwenden.O.k.
Und wie übergebe ich den $Ordner in den Import + Datei.csv? (hier Zeile 3)
Ich habs mal einfach mit -Path '$Ordner\datei.csv' versucht. Da crasht er aber.
$CSV = Import-CSV -Delimiter ';' -Path "$Ordner\Vorlage.csv" -Header (1..14)
Um das gleich vorweg zu nehmen: So werden nur Variablen selbst interpretiert, nicht aber Eigenschaften eines Objekts.
Wenn du z.B. den Ordnernamen auch aus einer CSV-Datei holst, musst du, um Eigenschaften ebenfalls auszuwerten, mit $() arbeiten (solltest du ja bereits von Bash kennen). Beispiel:
$CSV = Import-CSV -Delimiter ';' -Path "$($CSV.Ordner)\Vorlage.csv" -Header (1..14)
Zitat von @Hacktrist:
Thomas kannst du mir als letztes noch zeigen wie ich direkt die Datei inkusive Ordnerangabe (durch manuelle Pfadangabe) lade
Wenn ich das richtig verstehe, also mit kompletter Dateipfad-Eingabe?Thomas kannst du mir als letztes noch zeigen wie ich direkt die Datei inkusive Ordnerangabe (durch manuelle Pfadangabe) lade
# Frage nach Dateipfad, bis ein gültiger Pfad eingegeben wurde.
while ($true) {
$Pfad = Read-Host "Dateipfad eingeben"
if (Test-Path -PathType Leaf $Pfad) {
$Pfad = Get-Item $Pfad
break
}
else {write-host -ForegroundColor 'red' "'$Pfad' ist kein gültiger Dateipfad. Bitte erneut versuchen. "}
}
# CSV-Datei importieren (ipcsv ist ein Alias für Import-CSV)
$CSV = ipcsv -d ';' $Pfad.fullname -header (1..14)
# Mache was auch immer mit der CSV
# ...
# Neuer Dateiname
$NewName = $Pfad.DirectoryName + '\' + $Pfad.Basename + '_neu' + $Pfad.Extension
# CSV-Datei exportieren
($CSV | ConvertTo-CSV -nti -d ';') -replace '(?<=^|;)"|"(?=;|$)' | Select -Skip 1 | sc $NewName
Die Sinnhaftigkeit stelle ich hier allerdings mal infrage. Sowas würde ich...
- entweder, wie oben erwähnt, über einen grafischen Dialog lösen
- oder über ein Kontextmenüeintrag. So bräuchtest du nur im Explorer auf eine CSV-Datei rechtsklicken und könntest diese Datei über einen Eintrag direkt an das Skript senden.
Zitat von @Hacktrist:
Hey Thomas, das mit der graphischen Oberfläche ist ja cool. Habe bisher noch nicht gewusst, dass Power Shell so mächtig ist.
Noch viel mächtiger, als du derzeit ahnst 😎. Auch das erstellen komplett eigener GUI-Oberflächen für ein Skript ist ganz leicht möglich.Hey Thomas, das mit der graphischen Oberfläche ist ja cool. Habe bisher noch nicht gewusst, dass Power Shell so mächtig ist.
Nur Interesse halber, wie kann ich das geladene Objekt an den Rest des Scripts übergeben?
Zu dem Zweck war das mit dem Ergebnis anzeigen gedacht. Also einfach:$CSV = ipcsv -d ';' $Open.Filename -header (1..14)
Beachte allerdings, das es auch möglich ist, das der User das Öffnen der CSV-Datei abbricht, ohne eine Datei ausgewählt zu haben.
Alle weiteren Schritte würden dann natürlich zu einem Fehler führen - um das auszuschließen, einfach vorweg:
if (! $Open.Filename) {Return}
Zitat von @Hacktrist:
Es hängt jetzt in Zeile 23 oder 26 mit dem Fehler Der Zugriff auf den Pfad "C:\_neu" wurde verweigert
Das Objekt in deiner $Open-Variable enthält auch keine Eigenschaft wie DirectoryName,BaseName oder Extension.Es hängt jetzt in Zeile 23 oder 26 mit dem Fehler Der Zugriff auf den Pfad "C:\_neu" wurde verweigert
# Neuer Dateiname
$NewName = $Open.DirectoryName + '\' + $Open.Basename + '_neu' + $Open.Extension
Deshalb sagte ich, guck dir am besten immer in Powershell den Inhalt deiner Variable an.
In den meisten Fällen reicht es, einfach den Variablennamen auszuführen:
$Open
gibt dir dann folgende Ausgabe zurück:
CheckFileExists : True
Multiselect : False
ReadOnlyChecked : False
ShowReadOnly : False
SafeFileName : Datei.csv
SafeFileNames : {Datei.csv}
AddExtension : True
CheckPathExists : True
DefaultExt :
DereferenceLinks : True
FileName : C:\test\Datei.csv
FileNames : {C:\test\Datei.csv}
Filter : CSV-Dateien|*.csv|Alle Dateien|*.*
FilterIndex : 1
InitialDirectory :
RestoreDirectory : False
ShowHelp : False
SupportMultiDottedExtensions : False
Title : Öffne CSV-Datei
ValidateNames : True
CustomPlaces : {}
AutoUpgradeEnabled : True
Tag :
Site :
Container :
Das sind die Eigenschaften, die dein Objekt besitzt.
Hier bringt uns in dem Fall also lediglich die Filename-Eigenschaft weiter (oder die Filenames-Eigenschaft, wenn du vor dem starten des Dialogs die Multiselect-Eigenschaft auf $true setzt, um auch mehrere Dateien gleichzeitig auswählen zu können).
Nun sehen wir uns mal an, welcher Typ in der Eigenschaft Filename liegt:
$Open.Filename.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
Die Eigenschaft ist nur vom Typ [string], also liegt der Pfad hier lediglich als Zeichenkette vor, nicht als Objekt mit Eigenschaften.
Also laden wir die Datei aus dem Dateisystem:
$File = Get-Item $Open.Filename
Da ein Dateisystemobjekt recht viele Eigenschaften besitzt, wurden einige der Übersichtlichkeit halber ausgeblendet.
Man kann entweder mit Get-Member die Eigenschaften,Events und Methoden anzeigen lassen, die ein Objekt besitzt (allerdings nicht den Wert zu einer Eigenschaft)
$File | Get-Member
oder man verwendet wieder Select-Object, diesmal mit dem Parameter -Property *
$File | Select *
PSPath : Microsoft.PowerShell.Core\FileSystem::C:\test\Datei.csv
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\test
PSChildName : Datei.csv
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
Mode : -a----
VersionInfo : File: C:\test\Datei.csv
InternalName:
OriginalFilename:
FileVersion:
FileDescription:
Product:
ProductVersion:
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language:
BaseName : Datei
Target : {}
LinkType :
Name : Datei.csv
Length : 322
DirectoryName : C:\test
Directory : C:\test
IsReadOnly : False
Exists : True
FullName : C:\test\Datei.csv
Extension : .csv
CreationTime : 01.04.2021 12:25:25
CreationTimeUtc : 01.04.2021 10:25:25
LastAccessTime : 20.04.2021 11:40:41
LastAccessTimeUtc : 20.04.2021 09:40:41
LastWriteTime : 19.04.2021 16:27:46
LastWriteTimeUtc : 19.04.2021 14:27:46
Attributes : Archive
Wie man sieht besitzt unser Objekt nun sowohl die Eigenschaft DirectoryName mit dem DateiPfad, sowie BaseName mit dem Dateinamen ohne Erweiterung, als auch Extension mit der Dateierweiterung.
Daher:
$File = Get-Item $Open.FileName
$NewName = $File.DirectoryName + '\' + $File.Basename + '_neu' + $File.Extension
Du solltest wirklich mal den Anfängerleitfaden durchgehen, den ich oben gepostet habe. Da wird das alles noch viel verständlicher erklärt, als ich das hier mal eben runterbeten könnte.