Performance Problem Powershell
Hallo Zusammen,
erstmal vielen Dank an alle, ich habe schon viel lernen können beim passiven mitlesen
aber jetzt stehe ich vor einem Problem:
ein Kunde hat mir 138 .TSV (also Dateien mit Tab als Trenner) Dateien gegeben mit jeweils ca. 400 MB und ca. 1.000.000 Zeilen je Datei, also insgesamt 51GB und 135.894.981 Zeilen.
Diese Dateien haben alle die gleichen 11 Spalten und sind nicht sortiert.
Mein Kunde würde gerne diese Dateien in Jahresordner und darunter dann Monatsdateien haben.
Das Datum sieht nicht immer gleich aus (leider...), dass mein Script jetzt mal eine Datei mit "Jan" oder "January" und eine mit "Januar" erstellt ist mir (erstmal) egal:
hier nur ein paar Beispiele
Sat, 1 Jan 2000 12:40:07 +0000
Sat, 10 Mar 2001 08:16:36 +0800
Monday, 14 January 2013 17:00:31 GMT
Thu, 19 Mar 2009 12:27:23 +0000 (UTC)
Thu, 21 Jun 2012 18:00:37 +0100 (BST)
Sun, 13 May 2012 12:06:41 -0400 (Eastern Daylight Time)
Mon, 14 May 2012 09:29:02 -0400 (EDT)
Das folgende Script läuft auch ohne Probleme, nur halt sehr sehr langsam. Bei der Masse dürfte es ca. 2 Jahre dauern aber solange will mein Kunde verständlicherweise nicht warten :D
könnt ihr mir sagen warum das Script so langsam ist und mir eventuell Tipps geben (bitte habt Nachsicht mit mir, bin erst zwei Tage mit Powershell zugange
)? liegt es am Import-CSV?
ich hatte das ganze schon über Batch am laufen aber aufgrund der unterschiedlichen Datums Formatierungen und Sonderzeichen die in den Zellen vorhanden sind bin ich auf immer mehr Fehler gestoßen und habe mich dann zur Powershell verleiten lassen was auch eigentlich ganz gut geklappt hat, zumindest solange ich mit Testdaten gespielt habe :D
erstmal vielen Dank an alle, ich habe schon viel lernen können beim passiven mitlesen
ein Kunde hat mir 138 .TSV (also Dateien mit Tab als Trenner) Dateien gegeben mit jeweils ca. 400 MB und ca. 1.000.000 Zeilen je Datei, also insgesamt 51GB und 135.894.981 Zeilen.
Diese Dateien haben alle die gleichen 11 Spalten und sind nicht sortiert.
Mein Kunde würde gerne diese Dateien in Jahresordner und darunter dann Monatsdateien haben.
Das Datum sieht nicht immer gleich aus (leider...), dass mein Script jetzt mal eine Datei mit "Jan" oder "January" und eine mit "Januar" erstellt ist mir (erstmal) egal:
hier nur ein paar Beispiele
Sat, 1 Jan 2000 12:40:07 +0000
Sat, 10 Mar 2001 08:16:36 +0800
Monday, 14 January 2013 17:00:31 GMT
Thu, 19 Mar 2009 12:27:23 +0000 (UTC)
Thu, 21 Jun 2012 18:00:37 +0100 (BST)
Sun, 13 May 2012 12:06:41 -0400 (Eastern Daylight Time)
Mon, 14 May 2012 09:29:02 -0400 (EDT)
Das folgende Script läuft auch ohne Probleme, nur halt sehr sehr langsam. Bei der Masse dürfte es ca. 2 Jahre dauern aber solange will mein Kunde verständlicherweise nicht warten :D
könnt ihr mir sagen warum das Script so langsam ist und mir eventuell Tipps geben (bitte habt Nachsicht mit mir, bin erst zwei Tage mit Powershell zugange
ich hatte das ganze schon über Batch am laufen aber aufgrund der unterschiedlichen Datums Formatierungen und Sonderzeichen die in den Zellen vorhanden sind bin ich auf immer mehr Fehler gestoßen und habe mich dann zur Powershell verleiten lassen was auch eigentlich ganz gut geklappt hat, zumindest solange ich mit Testdaten gespielt habe :D
Foreach ($File in Get-ChildItem -Path "\\hier steht der Pfad wo die Daten abgelegt sind\") {
$File.FullName
$i = 0
$csv = Import-CSV $File.FullName -Delimiter ' '
$csv | %{
$eins = $_.'MESSAGE-ID'
$zwei = $_.'DATE'
$drei = $_.'FROM'
$vier = $_.'APPARENTLY-TO'
$fünf = $_.'BCC'
$sechs = $_.'CC'
$sieben = $_.'TO'
$acht = $_.'X-ZANTAZ-RECIP'
$neun = $_.'SUBJECT'
$zehn = $_.'X-ZANTAZDOCCLASS'
$elf = $_.'PATH'
$Meinmonat = $csv.DATE[$i].split(" ")[2]
$Meinjahr = $csv.DATE[$i].split(" ")[3]
if ( $Meinjahr -like '20??' )
{
$PATH = "\\hier steht der Pfad wo ich die Daten hinlege\$Meinjahr"
$PathFile = "$Path\$Meinmonat $Meinjahr.tsv"
}
else
{
$PATH = "\\hier steht der Pfad wo ich die Daten im Fehlerfall hinlege also wenn das Jahr nicht richtig ist\Fehler"
$PathFile = "$Path\Fehler.tsv"
}
if (!(Test-Path $PATH)) {New-Item -Path $PATH -ItemType Directory}
if (!(Test-Path $PathFile)) {New-Item $PathFile -ItemType file
"MESSAGE-ID DATE FROM APPARENTLY-TO BCC CC TO X-ZANTAZ-RECIP SUBJECT X-ZANTAZDOCCLASS PATH" >> $PathFile
}
"$eins $zwei $drei $vier $fünf $sechs $sieben $acht $neun $zehn $elf" >> $PathFile
$i++
}
Pause
}
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 1141377437
Url: https://administrator.de/forum/performance-problem-powershell-1141377437.html
Ausgedruckt am: 15.04.2025 um 09:04 Uhr
13 Kommentare
Neuester Kommentar
Hallo,
ja das könnte am import-csv liegen, du kannst es ja mal recht einfach testen indem du statt dem import einfach mal so etwas verwendest, Headers u. Delimter sind natürlich anzupassen.
Tu dir und uns einen gefallen, verwende Englische Variablennamen! Wenn es schon Deutsch sein muss dann ohne Umlaute das f?hrt nur zu Problemen
grüsse
ja das könnte am import-csv liegen, du kannst es ja mal recht einfach testen indem du statt dem import einfach mal so etwas verwendest, Headers u. Delimter sind natürlich anzupassen.
Tu dir und uns einen gefallen, verwende Englische Variablennamen! Wenn es schon Deutsch sein muss dann ohne Umlaute das f?hrt nur zu Problemen
$Fields = 'Date', 'Time', 'Action', 'Source', 'Destination', 'User'
$Delimiter = ' '
ForEach ($file in $files)
{
$CSVFirstRow = Get-Content -TotalCount 2 $file.FullName |
ConvertFrom-Csv -Delimiter $Delimiter
$CSVFields = $CSVFirstRow.psobject.Properties.Name
$FieldIndices = $Fields | Foreach-Object { $CSVFields.IndexOf($_) }
Get-Content $file.FullName -ReadCount 1000 |
Foreach-Object {
foreach($Line in $_){
[string]::Join(',', ($s.Split($Delimiter)[$FieldIndices]))
}
} |
Set-Content "$dst\NEW - $($file.BaseName).csv"
}
grüsse
Moin,
mache keine Pipe auf foreach-object, sondern eine foreach-Schleife (https://devblogs.microsoft.com/scripting/getting-to-know-foreach-and-for ..).
hth
Erik
mache keine Pipe auf foreach-object, sondern eine foreach-Schleife (https://devblogs.microsoft.com/scripting/getting-to-know-foreach-and-for ..).
hth
Erik
Hi,
wenn ich das richtig sehe, dann testest Du jetzt 135 Mio Mal, ob der Zielpfad und die Zieldatei vorhanden sind. Das kostest natürlich auch Zeit.
Ich würde entweder auf Verdacht alle potentiellen Ordner und Dateien im Voraus erstellen und diese dann auf Deine Weise füllen.
Oder es auf eine Exception ankommen lassen und dann in diesem Falle die entsprechend fehlende Datei erst erstellen. Bei angenommen 10 Jahren hättest Du max. 120 Exceptions. Das wäre aber immer noch besser, als 135 Mio Mal die Existenz der Dateien zu prüfen.
E.
Edit:
Angenommen, das Prüfen der Datei-Existenz dauert jeweils 100 ms. Dann sind das bei 135 Mio Mal
- 13,5 Mio s, oder
- 225.000 min, oder
- 3.750 h, oder
- 156 d
wenn ich das richtig sehe, dann testest Du jetzt 135 Mio Mal, ob der Zielpfad und die Zieldatei vorhanden sind. Das kostest natürlich auch Zeit.
Ich würde entweder auf Verdacht alle potentiellen Ordner und Dateien im Voraus erstellen und diese dann auf Deine Weise füllen.
Oder es auf eine Exception ankommen lassen und dann in diesem Falle die entsprechend fehlende Datei erst erstellen. Bei angenommen 10 Jahren hättest Du max. 120 Exceptions. Das wäre aber immer noch besser, als 135 Mio Mal die Existenz der Dateien zu prüfen.
E.
Edit:
Angenommen, das Prüfen der Datei-Existenz dauert jeweils 100 ms. Dann sind das bei 135 Mio Mal
- 13,5 Mio s, oder
- 225.000 min, oder
- 3.750 h, oder
- 156 d
Moin,
jetzt habe ich mir mal näher angeschaut, was Du da eigentlich treibst. Das geht viel einfacher und dann wahrscheinlich auch deutlich schneller.
Nur so nebenbei: Die Dinger heißen auch dann csv, wenn sie nicht mit Kommata separiert sind. Oder besser: Es gibt so gut wie keine CSV, die mit Kommata separiert ist.
Ok, macht aber nichts. Grundregel: Die Reihenfolge der Spalten und der Datensätze darf keine Rolle spielen. Wichtig: Alle Spalten sind in allen Dateien mit den gleichen Namen vorhanden.
Na wenn der Kunde das so will.
Das liegt daran, dass Du es nicht richtig machst.
hier nur ein paar Beispiele
Sat, 1 Jan 2000 12:40:07 +0000
Sat, 10 Mar 2001 08:16:36 +0800
Monday, 14 January 2013 17:00:31 GMT
Thu, 19 Mar 2009 12:27:23 +0000 (UTC)
Thu, 21 Jun 2012 18:00:37 +0100 (BST)
Sun, 13 May 2012 12:06:41 -0400 (Eastern Daylight Time)
Mon, 14 May 2012 09:29:02 -0400 (EDT)
Das, was ich gleich tue, sollte bei allen halbwegs gängigen Datumsformaten klappen. Da ich aus den von Dir gelieferten Daten schließe, dass es sich um Email-Header oder so handelt, sollten das gängige Formate sein. Mit den Beispielen hier habe ich es getestet.
Warum nicht?
Ich schreibe das mal um und kommentiere gleichzeitig Deinen Code:
Ich hoffe, ich habe alle Klammern an der richtigen Stelle zu gemacht und auch sonst keine Typos. Ausprobieren und falls es Fehler wirft, dann gerne nachfragen. Ich habe nicht getestet.
hth
Erik
jetzt habe ich mir mal näher angeschaut, was Du da eigentlich treibst. Das geht viel einfacher und dann wahrscheinlich auch deutlich schneller.
Zitat von @n1philipp:
ein Kunde hat mir 138 .TSV (also Dateien mit Tab als Trenner) Dateien gegeben mit jeweils ca. 400 MB und ca. 1.000.000 Zeilen
ein Kunde hat mir 138 .TSV (also Dateien mit Tab als Trenner) Dateien gegeben mit jeweils ca. 400 MB und ca. 1.000.000 Zeilen
Nur so nebenbei: Die Dinger heißen auch dann csv, wenn sie nicht mit Kommata separiert sind. Oder besser: Es gibt so gut wie keine CSV, die mit Kommata separiert ist.
Diese Dateien haben alle die gleichen 11 Spalten und sind nicht sortiert.
Ok, macht aber nichts. Grundregel: Die Reihenfolge der Spalten und der Datensätze darf keine Rolle spielen. Wichtig: Alle Spalten sind in allen Dateien mit den gleichen Namen vorhanden.
Mein Kunde würde gerne diese Dateien in Jahresordner und darunter dann Monatsdateien haben.
Na wenn der Kunde das so will.
Das Datum sieht nicht immer gleich aus (leider...), dass mein Script jetzt mal eine Datei mit "Jan" oder "January" und eine mit "Januar" erstellt ist mir (erstmal) egal:
Das liegt daran, dass Du es nicht richtig machst.
hier nur ein paar Beispiele
Sat, 1 Jan 2000 12:40:07 +0000
Sat, 10 Mar 2001 08:16:36 +0800
Monday, 14 January 2013 17:00:31 GMT
Thu, 19 Mar 2009 12:27:23 +0000 (UTC)
Thu, 21 Jun 2012 18:00:37 +0100 (BST)
Sun, 13 May 2012 12:06:41 -0400 (Eastern Daylight Time)
Mon, 14 May 2012 09:29:02 -0400 (EDT)
Das, was ich gleich tue, sollte bei allen halbwegs gängigen Datumsformaten klappen. Da ich aus den von Dir gelieferten Daten schließe, dass es sich um Email-Header oder so handelt, sollten das gängige Formate sein. Mit den Beispielen hier habe ich es getestet.
Das folgende Script läuft auch ohne Probleme, nur halt sehr sehr langsam. Bei der Masse dürfte es ca. 2 Jahre dauern aber solange will mein Kunde verständlicherweise nicht warten :D
Warum nicht?
Ich schreibe das mal um und kommentiere gleichzeitig Deinen Code:
# Ein paar Deklarationen für später:
# Der Pfad auf die Datei muss exisitieren. Die Datei selbst nicht.
$errorpath = "x:\da\wo\die\fehler\hinsollen\fehler.csv"
$path = "x:\da\wo\die\Unterordner\hinsollen"
$months = @("Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez")
Foreach ($File in Get-ChildItem -Path "\\hier steht der Pfad wo die Daten abgelegt sind\") {
# $File.FullName unnötig
# $i = 0
$csv = Import-CSV $File.FullName -Delimiter "`t"
# Hier kannst Du zwar auch einfach einen TAB machen.
# Aber das ist schwerer zu lesen und führt später nur zur Verwirrung. Deshalb besser `t.
# Den Backtick nimmt man in der Powershell da, wo man in vielen anderen Sprachen
# den Backslash nimmt.
# Das nennt man das Escape-Zeichen.
# $csv | %{ keine Pipe!
# $eins = $_.'MESSAGE-ID'
# $zwei = $_.'DATE'
# $drei = $_.'FROM'
# $vier = $_.'APPARENTLY-TO'
# $fünf = $_.'BCC'
# $sechs = $_.'CC'
# $sieben = $_.'TO'
# $acht = $_.'X-ZANTAZ-RECIP'
# $neun = $_.'SUBJECT'
# $zehn = $_.'X-ZANTAZDOCCLASS'
# $elf = $_.'PATH'
# $Meinmonat = $csv.DATE[$i].split(" ")[2]
# $Meinjahr = $csv.DATE[$i].split(" ")[3]
# Nein, das ist eher objektdesorientiert. Das brauchst Du alles nicht.
# Warum auch sollte man Variablen, die man schon hat, in neue Variablen schreiben?
foreach($line in $csv) {
# Speichern in einer neuen Variablen, um die Originaldaten zu erhalten
$date = $line.DATE
# Entfernen der evtl. störenden Zeitzonenangabe am Ende
$date = $date -replace "\S*$|\(.*\)",""
$month = $months[$(get-date("$date").month-1]
$year = $(get-date("$date")).year
# if ( $Meinjahr -like '20??' )
# Das ist nicht schön. Besser so:
if($year -lt 2000 -or $year -isnot [int32]) {
#Hier teste ich, ob die Zahl kleiner als 2000 ist
# oder evtl. auch gar keine Zahl. Dann Fehler.
$line | export-csv $errorpath -append -notypeinformation -force
else {
# Danke an @emeriks für den Hinweis. Wir machen hier mal lieber kein if, sondern try-catch
try {
$line | export-csv $path\$year\$month.csv -append -notypeinformation -erroraction continue
}
catch {
if (!(Test-Path $path\$year)) {New-Item -Path $PATH -ItemType Directory}
try {
$line | export-csv $path\$year\$month.csv -append -notypeinformation -erroraction continue
}
catch {
write-error "Ein unerwarteter Fehler ist aufgetreten!"
}
}
}
}
Ich hoffe, ich habe alle Klammern an der richtigen Stelle zu gemacht und auch sonst keine Typos. Ausprobieren und falls es Fehler wirft, dann gerne nachfragen. Ich habe nicht getestet.
hth
Erik

Zitat von @erikro:
Moin,
mache keine Pipe auf foreach-object, sondern eine foreach-Schleife (https://devblogs.microsoft.com/scripting/getting-to-know-foreach-and-for ..).
hth
Erik
Moin,
mache keine Pipe auf foreach-object, sondern eine foreach-Schleife (https://devblogs.microsoft.com/scripting/getting-to-know-foreach-and-for ..).
hth
Erik
Oder man kombiniert die Vorteile von der Pipeline mit der Geschwindigkeit von foreach indem man beides in ein schnelles "Foreach-ObjectFast" packt
function Foreach-ObjectFast
{
param
(
[ScriptBlock]
$Process,
[ScriptBlock]
$Begin,
[ScriptBlock]
$End
)
begin
{
$code = @"
& {
begin
{
$Begin
}
process
{
$Process
}
end
{
$End
}
}
"@
$pip = [ScriptBlock]::Create($code).GetSteppablePipeline()
$pip.Begin($true)
}
process
{
$pip.Process($_)
}
end
{
$pip.End()
}
}
Und warum nutzt ihr nicht die Multithreading-Vorteile? z.B. via Foreach-Object -parallel in der PS 7, wäre ja zu schade die restlichen Kerne brach liegen zu lassen und nur mit einem Kern zu arbeiten.
Moin,
gern geschehen. Poste mal das fertige Skript mit allen korrekten Klammern und ohne die vielen auskommentierten Zeilen. Dann baue ich Dir das noch ein. Eine Korrektur gleich:
In Zeile 62 und 70 die erroraction von "continue" auf "stop" ändern. Sonst läuft die Fehlerbehandlung nicht richtig. Da hatte ich an dem Tag einen Denkfehler.
Dann vielleicht noch
nach der Zeile 75.
Liebe Grüße
Erik
Zeit kostet das natürlich, wenn man mitloggt, in welcher Datei, in welcher Zeile man gerade ist.
Liebe Grüße
Erik
gern geschehen. Poste mal das fertige Skript mit allen korrekten Klammern und ohne die vielen auskommentierten Zeilen. Dann baue ich Dir das noch ein. Eine Korrektur gleich:
In Zeile 62 und 70 die erroraction von "continue" auf "stop" ändern. Sonst läuft die Fehlerbehandlung nicht richtig. Da hatte ich an dem Tag einen Denkfehler.
Dann vielleicht noch
$line | export-csv $errorpath -append -notypeinformation -force
nach der Zeile 75.
Liebe Grüße
Erik
Zeit kostet das natürlich, wenn man mitloggt, in welcher Datei, in welcher Zeile man gerade ist.
Liebe Grüße
Erik

Zitat von @n1philipp:
Ich habe grade gesehen, dass die powershell.exe mehr als 10 GB Memory für sich nutzt, ist das normal? auf dem Server waren nur noch ca. 30 bis 100 MB (schwankend) frei. Kommt daher eventuell der Fehler?
Ich habe grade gesehen, dass die powershell.exe mehr als 10 GB Memory für sich nutzt, ist das normal? auf dem Server waren nur noch ca. 30 bis 100 MB (schwankend) frei. Kommt daher eventuell der Fehler?
Wenn du für jede Zeile der Files so viele Variablen deklarierst ist klar das irgendwann der Speicher ausgeht und zwischendurch keine Garbage Collection machst
Wenn es wirklich um Geschwindigkeit geht mach ich sowas persönlich immer mit nem Bash-Script. sed/(g)awk sind was sowas angeht im Vergleich mit der PS rasend schnell und auf Zeilenverarbeitung spezialisiert.
Hab das gerade mal mit nem gawk Script gecheckt. Das erstellt aus einem Demo File mit 1.000.000 Zeilen auf nem 2,8GHz 8 Kern i7 in ca 20 Sekunden die entsprechenden csv files. Da kommt die Powershell auch mit Multithreading niemals hin. Für deine Anzahl wäre die Arbeit dann in ca. 45 Minuten erledigt, wenn man das ganze dann noch auf die Prozessorkerne auftteilt indem man es mehrfach startet lässt sich das nochmal um den Faktor x(Kernanzahl) reduzieren.
Bei 8 Kernen wäre die Aufgabe dann optimalerweise also in 5-6 Minuten erledigt, wenn das Ausgabe-Medium mitmacht.
Bei Interesse am gawk Script einfach bei mir per PN melden.
Moin,
etwas später. Aber ich hatte zu tun.
Das ist eine gute Idee. Warum hatte ich die nicht?
Und gawk gibt es ja sogar auch für Windows. Ist dann wahrscheinlich nur halb so schnell aber immer noch schneller als die PS. 
Och nööööööö. Bitte hier weitermachen.
Das ist mit Sicherheit von allgemeinem Interesse.
Liebe Grüße
Erik
etwas später. Aber ich hatte zu tun.
Zitat von @149062:
Wenn es wirklich um Geschwindigkeit geht mach ich sowas persönlich immer mit nem Bash-Script. sed/(g)awk sind was sowas angeht im Vergleich mit der PS rasend schnell und auf Zeilenverarbeitung spezialisiert.
Wenn es wirklich um Geschwindigkeit geht mach ich sowas persönlich immer mit nem Bash-Script. sed/(g)awk sind was sowas angeht im Vergleich mit der PS rasend schnell und auf Zeilenverarbeitung spezialisiert.
Das ist eine gute Idee. Warum hatte ich die nicht?
Bei Interesse am gawk Script einfach bei mir per PN melden.
Och nööööööö. Bitte hier weitermachen.
Liebe Grüße
Erik
Moin,
Zu Deiner Frage:
Nicht
sondern
Das, was Du geschrieben hast, ist nicht falsch. Das zweite ist aber einfach üblich seit C++. Jetzt weißt Du auch, warum C++ C++ heißt.
Dann nicht:
sondern
Genauso auch an den Stellen, an denen Du add-content benutzt. Merke: Die Pipe ist immer langsamer als die direkte Übergabe des Objekts an den Befehl. (Warum das hier -value und nicht -InputObject heißt, muss mir mal einer erklären.)
Dann solltest Du am Ende der foreach-Schleife mit remove-variable mindestens $i besser alle Varibalen, die in der Schleife erzeugt werden, vernichten, um den Arbeitsspeicher sofort freizugeben. Dazu wäre es hilfreich, die Variablennamen mit einem Prä- oder Suffix zu versehen, da der Befehl Wildcards im Variablennamen erlaubt.
Die Idee mit der bash und gawk ist aber deutlich zielführender.
hth
Erik
Zitat von @n1philipp:
$errorpath = "\\Pfad\fehler.csv"
> $path = "\\Pfad\"
> "Neubeginn der Protokolldatei " | set-content Status_Dateien.txt
> "Neubeginn der Protokolldatei " | set-content Status.txt
> [...]
> "Wir sind fertig" | add-content Status_Dateien.txt
> Pause
Zu Deiner Frage:
Nicht
$i = $i +1
$i++
Das, was Du geschrieben hast, ist nicht falsch. Das zweite ist aber einfach üblich seit C++. Jetzt weißt Du auch, warum C++ C++ heißt.
Dann nicht:
"$file $i" | set-content status.txt
# Ganz oben
$status = status.txt
[...]
set-content -value "$file $i" -path $status
Genauso auch an den Stellen, an denen Du add-content benutzt. Merke: Die Pipe ist immer langsamer als die direkte Übergabe des Objekts an den Befehl. (Warum das hier -value und nicht -InputObject heißt, muss mir mal einer erklären.)
Dann solltest Du am Ende der foreach-Schleife mit remove-variable mindestens $i besser alle Varibalen, die in der Schleife erzeugt werden, vernichten, um den Arbeitsspeicher sofort freizugeben. Dazu wäre es hilfreich, die Variablennamen mit einem Prä- oder Suffix zu versehen, da der Befehl Wildcards im Variablennamen erlaubt.
Die Idee mit der bash und gawk ist aber deutlich zielführender.
hth
Erik