Sehr große Datei in Teilschritten auslesen.

T

tibrandt

Grünschnabel
Hallo an das Forum,

ich brauche Eure Hilfe, der Groschen will bei mir nicht fallen:
Ich habe eine sehr große Datei mit „über“ 100000 Zeilen. Die Daten muss ich Zeilenweise auslesen und in eine DB einlesen.
Meine Lösung, diese Komplett in ein Array zu pumpen, funktioniert, ist aber wegen der Größe der Datei sehr langsam.
Deshalb möchte ich in 1000 Zeilen schritten das einlesen vornehmen:
Programmablauf soll sein, 1000 Zeilen einlesen, diese verarbeiten um denn die nächsten 1000 einzulesen, bis das Ende der Datei erreicht ist.

Mein Lösungsansatz ohne Schritte beginnt hier:
Code:
unset TEMPO
ZEILENNUM=1
for ((p=1; $p<=$(wc -l $import | awk '{print $1}'); p++));do
                TEMPO[ZEILENNUM]=$(head -n$p $import | tail -n1)
                ZEILENNUM=$(($ZEILENNUM+1))
done

Es ist bestimmt ganz einfach, ich sehe es aber leider nicht.

Nachtrag:
Eine Bash ist eigentlich nicht so optimal für die Bearbeitung solch großer Dateien. Aber mein Programm hätte die ganze Nacht Zeit dafür. Der Lösungsansatz soll nur zeigen, in welche Richtung ich will. Normal lese ich Dateien über eine Funktion ein z.B.
Code:
function transf()
{
ZEILENNUM=1
TRANF=$(cat "$1" | tr "'" ' ')
OLDIFS=$IFS
IFS=$'\n'
for foo in ${TRANF}; do
        MEM[ZEILENNUM]=$foo
        ZEILENNUM=$(($ZEILENNUM+1))
done
IFS=$OLDIFS
}

Über
Code:
transf "datei.csv"

geht das einlesen in das Array schon sehr schnell, aber das verarbeiten nicht.

Danke!
 
Zuletzt bearbeitet:
Hallo,

zum Aufteilen einer Datei gibt es das nette Kommando "split".

mfg
HeadCrash
 
Danke, das war die Lösung!

Gruß
tibrandt
 
auch wenn du schon eine Lösung hast verstehe ich nicht warum die ganze Datei im Memory haben willst (vllt gibt es ja Gründe) . ich würde das so abarbeiten, statt dem echo käme dann die Erledigung der Zeile


Code:
$ cat test.sh
#!/bin/bash

i=1
old_IFS=$IFS
IFS=$'\n'
for line in $(cat "$1")  
do  
   echo "=Zeilennr: $((i++))==$line==="  
done  
IFS=$old_IFS

Aufruf ist dann:
Code:
test.sh grossedatei.txt
 
Hi,

also mein Ansatz wäre etwa der folgende (im Beispiel einfach numerierte Ausgabe von 10er-Blöcken aus der /etc/passwd:
Code:
#!/bin/bash


DATA=/etc/passwd
CHUNKSIZE=10


process() {
        for ARG in "$@"; do
                echo "$ARG"
        done | pr -t -n
}


declare -a LINES

I=0
exec 3<$DATA

while read LINE <&3; do
        LINES[$I]="$LINE"
        let I++

        [ $(($I % $CHUNKSIZE)) = 0 ] && {
                process "${LINES[@]}"
                LINES=()

                echo
                I=0
        }
done

process "${LINES[@]}"

@tibrandt:

In deinem Original-Ansatz fällt mir zunächst einmal auf, dass du bei jedem Schleifendurchlauf die ganze Eingabedatei (wegen $(wc -l $input) einmal komplett einliest; das liesse sich einfach dadurch beheben, dass du die Länge der Eingabe schon vor dem Eintritt in die Schleife bestimmst. (Statt mit 'awk' kannst du dabei das eigentliche Ergebnis direkt bekommen, wenn du 'wc -l <$input' verwendest.)

Richtig langsam dürfte das ganze aber dadurch werden, dass du in der Loop die Eingabe (mit 'head') immer wieder vom Anfang bis zu der Zeile liest, die dich gerade interessiert - deine Laufzeit geht also mit dem Quadrat der zu verarbeitenden Zeilen hoch ...

@allesmueller:

Liest du mit $(cat "$1") nicht auch die ganze Datei in den Speicher ein?

Grüsse, A.
 
floyd62, du hast mich beim Nichtnachdenken ertappt, Mist :)
Deine Lösung ist ganz klar zu bevorzugen.

Bestenfalls könnte man noch mit
Code:
cat $1 | while read line; do
   echo $line
done
arbeiten. Aber das liest zeichenweise ein - suboptimal - ausserdem ein Bsp für useless use of cat :)
 
Hallo,
man kann so eine große Datei heutzutage auch einfach so in eine Datenbank laden. Dafür hält jede größere Datenbank ein eigenes Tool bereit:

MySQL - mysqlimport
Oracle - SQL*Loader
Sybase - bcp
...

Damit dürfte der Import von 100.000 Zeilen auf einen Schlag keine Probleme bereiten. Ich nehme mal an, dass bei dieser Datei mit den über 100.000 Zeilen jede Zeile gleich aufgebaut ist (Felder getrennt durch Komma oder andere Trennzeichen), nur dass nicht immer alle Felder gefüllt sind.
Man erstellt in der Datenbank eine Import-Tabelle, die ungefähr so aufgebaut ist wie die Datei. D.h. mindestens die Felderanzahl muss stimmen. Man kann die Felder aber auch gleich schon mal entweder als Text oder Zahlen formatieren.
Also Hauptsache die Daten sind irgendwie schon mal in der Datenbank drinnen. Eine (große) Datenbank (meistens auf einem schnellen Rechner oder Server) hat sehr effektive Mittel, um dann die Daten weiterzuverarbeiten.
 
Willkommen im Unixboard,

der Thread ist 8 Monate alt, bitte grab keine Leichen aus.
 
Hallo marcellus,
Danke für die Begrüßung. Ich gebe zu ich habe nicht geschaut, ob irgendwo anders diese Möglichkeit des bulk-copy erwähnt worden ist. Ich werde das noch nachholen, falls vorhanden, und dort meinen Beitrag reinstellen. Aber manchmal lohnt es sich ja trotzdem auch in einem alten Thread zu antworten, wenn es z.B. sonst noch keinen richtigen Platz gibt. Die Zahl der Klicks
 

Ähnliche Themen

Prblem mit zeilenweises auslesen von Datei und schreiben nach mysql

Schleife zum einlesen vieler Dateien funktioniert nicht

Nach editieren auf Linux Server läuft Ubuntu-Script nichtmehr?

Datei in shellscript Zeilenweise einlesen

bash: Fehlermeldung und Werte in Array sortieren?

Zurück
Oben