Textdatein in Unterordnern suchen und texte ersetzen per powershell?
Hallo ,ich habe einen Ordner auf dem Desktop, in dem ordner sind ca 500 weitere ordner und in jedem Ordnersind text datein .txt .... In den textdatein steht überall eine email adresse und diese will ich mit einer anderen email ersetzen ,habe schon gegoogelt aber nix gefunden was klappt
Also in einem Ordner sind ca 500 Unterordner in denen jeweils 2-3 text datein drinn sind wo als text eine email adresse ist die mit einer neuen email adresse ausgetauscht werden soll
2 Antworten
die ordner die einen sehr langen namen haben da geht es nicht
Für lange Pfade bist Du selbst verantwortlich, das hat nichts mit Powershell zu tun.
theoretisch könntest Du mit 8.3- Namen arbeiten, das ist jedoch unter Powershell nicht mehr vorgesen . Solche KurzNamen sind ein Relikt aus Dos-Zeiten und nicht mehr auf jedem Rechner verfügbar. Du kannst versuchen mit hilfe der alten VisualbasicScript-Bibliotken den evtl. vorhandenen Kurzpfad zu ermitteln und damit zu arbeiten.
$fso = New-Object -ComObject Scripting.FileSystemObject
Get-ChildItem -Path 'c:\Testpfad' -Recurse -Filter "*.txt" |
ForEach-Object {
$shortPath =$fso.getfile($_.fullname).ShortPath #ermittle 8.3-Pfad
Write-Host "$($_.FullName) => $shortPath" -fo green
# Powershells -replace-Operator verwendet ReguläreAusdrücke darum für statische Ersetzungen besser die .Replace()-Methode verwenden...
(Get-Content $shortPath -RAW).Replace('test@test.com','replaced@test.com')|
Set-Content $shortPath
}
..ansonsten bleibt dir nur die Umstellung des Systems auf lange Pfade. (Admin erforderlich!)
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
Dann geht der Klassiker fehlerfrei.
Get-ChildItem -Path 'c:\basispfad' -Recurse -Filter "*.txt" |
ForEach-Object {
(Get-Content $_.FullName -RAW).Replace('test@test.com','replaced@test.com')|
Set-Content $_.FullName
}
Keine Ahnung wie groß die Dateien sind auch so sind über 1000 eine ziemliche Hausnummer. ...und es ist unvermeidlich, das die Dateien in den speicher geladen werden und nach dem Begriff zum austauschen durchsucht werden ...und natürlich wieder zurückgeschrieben werden...
etwas schneller geht es, wenn man die Liste der ermittelten Textdateien auf mehrere Jobs verteilt und das Durchsuchen der Dateien (Replace) den Workern der Jobs überlässt.
function Get-PartSizes ( [Long]$Size , [Long]$Parts ) {
$Quotient=[Math]::Floor($Size/$parts)
$Remainder=$Size%$parts
$(for ($i=0;$i -lt $parts; ++$i){
if ($Remainder){
$Quotient+1
--$Remainder
}
else{$Quotient}
})|? {$_}
}
#was die Jobs tun sollen (das gleiche wie im oberen Script)
$Job_Worker = {
param(
[Array]$FileList=@()
)
$FileList |
ForEach-Object {
"verarbeite Datei $_" #nur mal ein Lebenszeichen des Jobs aus dem Hintergrund
try { #leere dateien abfangen "Nichts" kann keine Methode .Replace() besitzen
(Get-Content $_ -RAW).Replace('test@test.com','replaced@test.com')|
Set-Content $_
}
catch {"File seams empty"}
}
}
#Anzahl der parallelen Jobs festlegen (Hier Automatisch, kanst aber eine Dir genehme Zahl festlegen)
$JobCount=$env:NUMBER_OF_PROCESSORS
#Liste aller .txt -Dateien
[Array]$SearchFiles =(Get-ChildItem -Path 'c:\basepath' -Filter *.txt -Recurse -ea Silent).FullName
#Dateiliste in einigermaßen gleich große Teile zerlegen
$ListSlices=Get-PartSizes $SearchFiles.Count $JobCount |
?{$_}|
%{$base=0}{
,$SearchFiles[$base..($base+$_-1)] #aufpassen, Comma-Operator ...als neues Array übergeben
$base+=$_
}
#Jobs starten
$Jobs = $ListSlices|
%{ Start-Job -ScriptBlock $Job_Worker -ArgumentList (,$_) }
#$Jobs
#auf vollzug warten
$Jobs| Receive-Job -wait
pause
Nicht wundern, das verteilen der Dateien auf die einzelnen Jobs und deren Initialisierung nimmt etwas Zeit in Anspruch. einmal im Gang machen 10..20 parallele Verarbeitungseinheiten jedoch ordentlich Dampf
Mir würde es auch Reichen wenn einfach von allen txt Dateien die ersten 20 Zeilen gelöscht werden , wäre das einfacher umsetzbar?
sind das normal windws-Textdateien oder welche die unter Linux/Apple erstellt wurden?
Alles normale Windows Textdateien .txt
Get-ChildItem -Path 'C:\basepath' -Filter '*.txt' -Recurse -ea sil|
%{
$NewText = Get-Content $_.FullName|
Select-Object -Skip 20
$NewText|
Set-Content $_.FullName
}
...aber aufgepasst, die Routine schneidet gnadenlos bei allen .txt.Dateien die ersten 20 Zeilen ab! Kürzere datein werden geleert.
Sind alle Dateien gleich in den ersten 20 Zeilen ist wie so eine Art FAQ und der soll weg ,teste es gleich Mal aus , und der sucht dann auch die Unterordner ab ?
find . -type f -name "*.txt" -exec sed -i '' 's/alte@email.com/neue@email.com/g' {} +
Dieser Befehl durchsucht alle Textdateien in den Unterordnern auf deinem Desktop und ersetzt alte@email.com durch neue@email.com. Stelle sicher, dass du vorher eine Sicherungskopie deiner Daten hast, falls etwas schiefgeht.
find : Datei *.txt nicht gefunden
In Zeile:1 Zeichen:1
+ find . -type f -name "*.txt" -exec sed -i '' 's
Get-ChildItem -Path "C:\Pfad\Zum\Ordner" -Recurse -Filter "*.txt" | ForEach-Object {
(Get-Content $_.FullName) | ForEach-Object {
$_ -replace "alte@email.com", "neue@email.com"
} | Set-Content $_.FullName
}
Sowas in die Richtung kann das funktionieren?
das klappt , aber die ordner die einen sehr langen namen haben da geht es nicht da kommt dann immer ein fehler ,ordner mit einem kurzen namen klappt es perfekt
Dort geht es nicht weil Sonderzeichen drinnen vorkommen denke ich, die müsstest du das Script etwas anpassen, so eventuell:
Get-ChildItem -Path "C:\Pfad\Zum\Ordner" -File -Recurse -Filter "*.txt" | ForEach-Object {
$newPath = [System.IO.Path]::Combine($_.DirectoryName, [System.IO.Path]::GetRandomFileName())
Move-Item $_.FullName $newPath
(Get-Content $newPath) | ForEach-Object {
$_ -replace "alte@email.com", "neue@email.com"
} | Set-Content $_.FullName
Move-Item $newPath $_.FullName
}
-replace "alte@email.com", "neue@email.com"
Dir ist schon klar, das der -replace-operator mit einer RegEx-suche arbeitet und den Punkt als "anyCharacter" wertet?
der Punkt in einem suchausdruck sollte deshalb escaped werden! :
$string -replace "alte@email\.com", "neue@email.com"
Hast du WSL aktiv?
wer braucht WSL (Windows-Subsystem für Linux), wenn er Powershell kann?
echt mal... das löst übrigens auch nicht das Problem der Langen Pfade...
hi alles klappt nun aber sehr sehr langsam arbeitet er alles ab ,kann man das beschleunigen irgendwie ?
es gibt ja noch den befehl
Foreach-ObjectFast
aber leider funktioniert der bei mir in powershell nicht, es ist extrem lahm das abarbeiten der datein,hat jemand ne lösung
habe alle ordner umbenannt , nun geht es aber sehr langsam arbeitet er alles ab ,glaube brauche 2 tage bei der geschwindigkeit .. kann man das beschleunigen ?