thor2511
Goto Top

Powershell Dateien nach Überprüfung in Ordnerstruktur einsortieren

Hallo,

dies ist mein erster Post hier, also seit bitte nachsichtig mir.

Ich habe eine *csv-Datei die so aussieht

Vorname;Name;ID
Anton;Tunichtgut;TA29241M
Alf;Melmac;MA28943M
Klaus;Schlau;SK29915M

und hier die Ordnerstruktur
ornderstruktur

und drei Dateien, die ich mit Hilfe von Powershell aus der CSV-Datei kopiert und umbenannt habe:

Alf-Melmac-MA28943M.xlsx
Klaus-Schlau-SK29915M.xlsx
Anton-Tunichtgut-TA29241M.xlsx

Die Spalte ID aus der CSV-Datei steht in den drei Dateien am Ende und ist jeweils der Name der einzelnen
Ordner unter TEST-Local

Ich möchte jetzt Folgendes erreichen.

1. Powershell soll die letzte Spalte der drei Dateien mit den Ordnernamen vergleichen
2. Wenn die Namen übereinstimmen soll es die entsprechende Datei in den jeweils passenden Unterordner (GROTHE) verschieben.
also so: Klaus-Schlau-SK29915M.xlsx gehört in TEST-Local\SK29915M\GROTHE

Ich weiß, dass man mit

$csv = Import-CSV 'Z:\Schule\Klausuren\Klassen\TEST\00-Test-ClassData.CSV' -Delimiter ';'  
$csv | %{
    Write-Host $_.ID
    # etc
    }

die CSV zeilenweise auslesen kann. Und mit
$folders = Get-ChildItem -Path Z:\Schule\Klausuren\Klassen\TEST\TEST-Local\

kann man die Ordnerstruktur in einer Variable speichern.

Aber wie bekomme ich jetzt das Vergleichen und entsprechende Verschieben in die Unterordner hin?

Für Hilfe wäre ich sehr dankbar!

VG Thorsten

Content-Key: 1620263915

Url: https://administrator.de/contentid/1620263915

Printed on: April 26, 2024 at 00:04 o'clock

Member: TK1987
Solution TK1987 Dec 14, 2021 updated at 20:18:42 (UTC)
Goto Top
Moin Thorsten und herzlich willkommen im Forum.

Zitat von @thor2511:
Aber wie bekomme ich jetzt das Vergleichen und entsprechende Verschieben in die Unterordner hin?
Für sowas nutzt man am besten den Befehl Where-Object.

Der Befehl prüft, ob eine Bedingung gegeben ist; und spukt am Ende nur noch die zutreffenden Objekte wieder aus.
# Quellpfad, in dem die Ordner und Dateien liegen
$Source = 'Z:\Schule\Klausuren\Klassen\TEST\TEST-Local'  

# Prüfen, ob im Quellpfad Ordner mit Name aus Spalte "ID" vorhanden sind 
$CSV | Where-Object {Test-Path -Type Container "$Source\$($_.ID)\GROTHE"} | Foreach-Object {  

  # Dateinamen aus CSV-Zeile generieren
  $Name = "$($_.Vorname)-$($_.Name)-$($_.ID).xlsx"  
  
  # Prüfen, ob XLSX-Datei im Quellpfad existiert - falls ja verschieben
  if (Test-Path "$Source\$Name") { Move-Item -Path "$Source\$Name" -Destination "$Source\$($_.ID)\GROTHE" }  
 
} # Ende der Foreach-Schleife

back-to-topHinweis:
Ich weiß, dass man mit
$csv | %{
    Write-Host $_.ID
    # etc
    }
die CSV zeilenweise auslesen kann.
das % ist ein Alias, also eine Kurzform für den Befehl Foreach-Object.
Genauso kann man auch statt Where-Object dessen Alias ? nutzen.

Gruß Thomas

PS: Hast du schon mal einen Blick in unseren Powershell Leitfaden für Anfänger geworfen?
Member: thor2511
thor2511 Dec 14, 2021 at 21:06:03 (UTC)
Goto Top
Danke schön für die super schnelle Antwort, teste ich gleich und melde mich. Den Leitfaden kannte ich definitiv noch nicht, komme vom Latex-Programmieren aber seh ich mir auf jeden Fall an!
Mitglied: 149569
149569 Dec 15, 2021 updated at 09:22:29 (UTC)
Goto Top
Wenn die Dateien schon umbenannt sind reicht auch ein
$source = 'Z:\Schule\Klausuren\Klassen\TEST\TEST-Local'  
Get-ChildItem $source -File | ?{Test-Path "$source\$($_.Basename.split('-')[-1])\GROTHE"} | move-item -Destination {"$source\$($_.Basename.split('-')[-1])\GROTHE"} -verbose  
Member: thor2511
thor2511 Dec 15, 2021 updated at 21:42:27 (UTC)
Goto Top
So nach einem Tag Arbeit läuft es nun. Ich habe noch einige Anpassungen vornehmen müssen, wie z.B. Umlaute und Leerzeichen in der CSV ersetzen, Variablen definieren und einige weitere Kleinigkeiten. Da ich Anfänger bin, hat es eben länger gedauert. Aber es läuft. Ziemlich cool, weil mir das sehr viel Arbeit abnimmt.
Jetzt muss ich noch herausfinden, wie man das Ganze auch für mehrere Dateien auf einmal machen kann.

So sieht es jetzt aus
# Arbeitsverzeichnis nur für Powershell ISE
$WorkingDir = "Z:\Schule\Klausuren\Klassen\TEST"  

# Nur für Powershell ISE
Set-Location $workingDir

##---Variablen anpssen---##

# Quellpfad, in dem die Ordner und Dateien liegen
$Folders = "Z:\Schule\Klausuren\Klassen\TEST\TEST-Local"  

# Name Unterverzeichnis
$instrName = "GROTHE"  

# Source Files
$Files = "HHU-2.4-Wenn-Diagr.xlsx"  

# CSV-Datei
$CSV = "00-Test-ClassData.csv"  

##---ab hier nichts ändern---##

# Dateityp auslesen
$FileType = [System.IO.Path]::GetExtension("$Files")  

# Umlaute und Leerzeichen in CSV ersetzen
$CSVNoUmlauts = Get-Content $CSV
 Foreach ($i in $CSVNoUmlauts) {
 $NoUmlauts = $i.Replace('ä','ae').Replace('Ä','Ae').Replace('ö','oe').Replace('Ö','Oe').Replace('ü','ue').Replace('Ü','Ue').Replace('ß','ss').Replace(' ','')  
 Add-Content -Path .\NoUmlauts.csv -Value $NoUmlauts
 }

# Alte CSV mit neuer ersetzen
Move-Item .\NoUmlauts.csv -Destination $CSV -Force 

# CSV nach Spalten sortieren: Vorname;Name;ID
# sonst funktioniert das Einsortieren nicht
# so ist es egal, wie die CSV vorher sortiert ist
Import-Csv $CSV -Delimiter ';' | Select Vorname, Name, ID | export-csv CSV-Sortiert.csv -Delimiter ';' -NoTypeInformation  

# Anführungszeichen in CSV entfernen
$CSVSortiert = Get-Content "CSV-Sortiert.csv"   
$CSVSortiert = $CSVSortiert.Replace('"','') | Set-Content .\CSV-Sortiert.csv -Force  

# Orginal CSV danach übercshreiben
Move-Item .\CSV-Sortiert.csv -Destination $CSV -Force 

# Zähler für verschobene Dateien auf 0 setzen
$numCopied = 0

# Temporäre CSV ohne Header erstellen
Get-Content "$CSV" | Select-Object -Skip 1 | Set-Content Temp.csv  

# Dateinamen aus CSV einlesen, Quelldatei dublizieren und umbenennen. Temp.csv danach löschen
Import-CSV .\Temp.csv -Header newFileName | % { Copy-Item -Path .\$Files -Destination "$Folders\$($_.newfilename -replace ';','-' )$FileType" }   
Remove-Item .\Temp.csv

# CSV einlesen
$CSV = Import-CSV "$CSV" -Delimiter ';'  

# Prüfen, ob im Quellpfad Ordner mit Name aus Spalte "ID" vorhanden sind 
$CSV | Where-Object {Test-Path -Type Container "$Folders\$($_.ID)\$instrName"} | Foreach-Object {  

  # Dateinamen aus CSV-Zeile generieren
  $Name = "$($_.Vorname)-$($_.Name)-$($_.ID).xlsx"  
  
  # Prüfen, ob XLSX-Datei im Quellpfad existiert - falls ja verschieben
  if (Test-Path "$Folders\$Name") { Move-Item -Path "$Folders\$Name" -Destination "$Folders\$($_.ID)\$instrName" }   

  # Zähler bei jedem Durchlauf +1
  $numCopied ++   
   
} # Schleife endet hier

# Summe verschobener Dateien
Write-Host Dateien verschoben: "$numCopied"  

Bin ja schon ein wenig stolz, dass ich das in meinem Alter noch hinbekomme habe face-smile. Allerdings brauche ich immer einen Anstoß, alleine wäre ich da nicht drauf gekommen...
Member: TK1987
TK1987 Dec 16, 2021 updated at 11:48:12 (UTC)
Goto Top
Zitat von @thor2511:
So sieht es jetzt aus
# Umlaute und Leerzeichen in CSV ersetzen
$CSVNoUmlauts = Get-Content $CSV
 Foreach ($i in $CSVNoUmlauts) {
 $NoUmlauts = $i.Replace('ä','ae').Replace('Ä','Ae').Replace('ö','oe').Replace('Ö','Oe').Replace('ü','ue').Replace('Ü','Ue').Replace('ß','ss').Replace(' ','')  
 Add-Content -Path .\NoUmlauts.csv -Value $NoUmlauts
 }
Dafür braucht man kein Foreach zu nutzen, die Replace-Methode lässt sich auch einfach auf Arrays anwenden
# Dateiinhalt lesen, Umlaute ersetzen.
$CSVNoUmlauts = (Get-Content $CSV).Replace('ä','ae').Replace('Ä','Ae').Replace('ö','oe').Replace('Ö','Oe').Replace('ü','ue').Replace('Ü','Ue').Replace('ß','ss').Replace(' ','')  

# Alte CSV mit neuer ersetzen
Move-Item .\NoUmlauts.csv -Destination $CSV -Force 

# CSV nach Spalten sortieren: Vorname;Name;ID
# sonst funktioniert das Einsortieren nicht
# so ist es egal, wie die CSV vorher sortiert ist
Import-Csv $CSV -Delimiter ';' | Select Vorname, Name, ID | export-csv CSV-Sortiert.csv -Delimiter ';' -NoTypeInformation  
Die Datei erst zu exportieren und anschließend erneut zu importieren ist unnötig. Du kannst deine Text-Datei einfach konvertieren und umsortieren. Merke: Dateien immer nur ein mal Importieren/Exportieren.
$CsvSortiert = $CSVNoUmlauts | ConvertFrom-CSV -Delimiter ';' | Select Vorname, Name, ID  

# Anführungszeichen in CSV entfernen
$CSVSortiert = Get-Content "CSV-Sortiert.csv"   
$CSVSortiert = $CSVSortiert.Replace('"','') | Set-Content .\CSV-Sortiert.csv -Force  
Wozu? Anführungszeichen sind im CSV-Dateiformat als Feldbegrenzer definiert - und werden sogar zwingend erforderlich, wenn bestimmte Zeichen in der Datei vorhanden sind.
CSV-Dateiformat#Dateiaufbau

# Orginal CSV danach überschreiben
Move-Item .\CSV-Sortiert.csv -Destination $CSV -Force
Mit Set-Content kann man die Datei auch einfach direkt überschreiben.

Jetzt muss ich noch herausfinden, wie man das Ganze auch für mehrere Dateien auf einmal machen kann.
Foreach kennst du doch schon 😉
# Arbeitsverzeichnis
$WorkingDir = "Z:\Schule\Klausuren\Klassen\TEST\TEST-Local"  

# Name Unterverzeichnis
$instrName = "GROTHE"  

# Alle CSV-Dateien auflisten
$CSVs = Get-ChildItem -File "$WorkingDir\*.csv"  

# Zähler für bearbeitete Dateien
$i = 0

# Für jede Csv-Datei...
Foreach ($CSV in $CSVs) {
  
  # Inhalt lesen, Umlaute ersetzen
  $Content = (Get-Content $CSV).Replace('ä','ae').Replace('Ä','Ae').Replace('ö','oe').Replace('Ö','Oe').Replace('ü','ue').Replace('Ü','Ue').Replace('ß','ss').Replace(' ','')  
  
  # Text in Powershell-Objekt konvertieren
  $Object = $Content | ConvertFrom-CSV -Delimiter ';'  
  
  # Für Jede Zeile des Objekts, zu der ein Ordner im Arbeitsverzeichnis existiert...
  Foreach ($Line in $Object | ? {Test-Path -Type Container "$WorkingDir\$($_.ID)\$instrName"} ) {  
    
    # Dateiname aus Spalten generieren
    $Name = $Line.Vorname + '-' + $Line.Nachname + '-' + $Line.Id + '.xlsx'  
    
    # Prüfen, ob XLSX-Datei im Quellpfad existiert - falls ja, verschieben und Zähler erhöhen
    if (Test-Path "$WorkingDir\$Name") {  
      Move-Item -Path "$WorkingDir\$Name" -Destination "$WorkingDir\$($_.ID)\$instrName"  
      $i++
    }
    
  } # Ende Foreach Line
  
} # Ende Foreach CSV

write-host "$i Dateien verschoben."  

Gruß Thomas
Member: thor2511
thor2511 Dec 16, 2021 at 11:28:11 (UTC)
Goto Top
Wow, da macht sich wirklich jemand die Mühe, einem absolutem Anfänger etwas zu erklären. Super und vielen Dank, jetzt muss ich den ganzen Input erst einmal verarbeiten. Ich melde mich wieder, wenn ich alles verstanden um umgesetzt habe....
Member: thor2511
thor2511 Dec 23, 2021 updated at 23:06:11 (UTC)
Goto Top
So, ich habe jetzt einige Zeit gebastelt und mit Deiner Hilfe ein lauffähiges Script, was für eine *.*-Datei gut funktioniert. Nochmals vielen Dank dafür!!

Eine Sache bekomme ich einfach nicht hin. Ich möchte gerne, dass mehrere Dateien aus einem Verzeichnis in einem Durchgang umbenannt und verschoben werden. Hier geht es um mehrere Quelldateien nicht um mehrere CSV's. Da hab ich mich missverständlich ausgedrückt.

Ich verstehe, dass man dafür zwei Schleifen braucht. Damit die Dateien sich nicht überschreiben, wäre es sinnvoll je Durchlauf eine Nummer als Präfix zu vergeben.

Also
Durchlauf 1 für die erste Datei
1-Name-Vorname-ID.xlsx , ...

Durchlauf 2 für die zweite Datei
2-Name-Vorname-ID.xlsx, ...

etc.

Aber ich bekomme es nicht hin, je Durchlauf eine Nummer zu vergeben. Hier habe ich die wesentlichen Auszüge aus deinem Script gepostet. Weil ich gerne ein Gui-Script haben möchte, habe ich mit WinForms gearbeitet und deine Anregungen eingebaut, deshalb auch nur der Auszug des Scriptes, sonst wird es zu unübersichtlich. Sobald es fertig ist, poste ich das ganze Script.

Ich hoffe, du/ihr versteht, was ich meine....

# Verzeichnis für Fremddateien
 $fileDir = "Files-Other"  

# Prüfen, ob $filedir existiert
If(!(test-path $classPath\$fileDir ))
{ New-Item -ItemType Directory -Force -Path "$fileDir" }  

# Quelldatei ohne Pfadangabe, sonst geht es nicht
$fileName = Get-ChildItem -File ".\$fileDir\*.*" | % { ($_.name) }  

# Kompletter Dateiname
$file =  $fileDir + "\$fileName"  


#Für jede Datei aus $filedir
Foreach ($file in $files) {
  
  Wie kann ich hier eine ID für alle Dateien je Durchlauf vergeben????
  
  # Für Jede Zeile des Objekts, zu der ein Ordner im Arbeitsverzeichnis existiert...
  Foreach ($Line in $Object | ? {Test-Path -Type Container "$WorkingDir\$($_.ID)\$instrName"} ) {  
    
    # Dateiname aus Spalten generieren
    $Name = $Line.Vorname + '-' + $Line.Nachname + '-' + $Line.Id + '.xlsx'  
    
    # Prüfen, ob XLSX-Datei im Quellpfad existiert - falls ja, verschieben und Zähler erhöhen
    if (Test-Path "$WorkingDir\$Name") {  
      Move-Item -Path "$WorkingDir\$Name" -Destination "$WorkingDir\$($_.ID)\$instrName"  
      $i++
    }
    
  } # Ende Foreach Line
  
} # Ende Foreach files

Ich muss echt noch einiges lernen .....

VG Thorsten