Ich habe ein seltsames Problem mit großen Dateien und bash
. Dies ist der Kontext:
- Ich habe eine große Datei: 75G und mehr als 400.000.000 Zeilen (es ist eine Protokolldatei, meine schlechte, ich lasse es wachsen).
- Die ersten 10 Zeichen jeder Zeile sind Zeitstempel im Format JJJJ-MM-TT.
- Ich möchte diese Datei aufteilen: eine Datei pro Tag.
Ich habe es mit dem folgenden Skript versucht, das nicht funktioniert hat. Meine Frage ist, dass dieses Skript nicht funktioniert, keine alternativen Lösungen .
while read line; do
new_file=${line:0:10}_file.log
echo "$line" >> $new_file
done < file.log
Nach dem Debuggen habe ich das Problem in der new_file
Variablen gefunden. Dieses Skript:
while read line; do
new_file=${line:0:10}_file.log
echo $new_file
done < file.log | uniq -c
gibt das Ergebnis wie folgt aus (Ich setze die x
es, um die Daten vertraulich zu behandeln, andere Zeichen sind die wirklichen). Beachten Sie die dh
und die kürzeren Saiten:
...
27402 2011-xx-x4
27262 2011-xx-x5
22514 2011-xx-x6
17908 2011-xx-x7
...
3227382 2011-xx-x9
4474604 2011-xx-x0
1557680 2011-xx-x1
1 2011-xx-x2
3 2011-xx-x1
...
12 2011-xx-x1
1 2011-xx-dh
1 2011-xx-x1
1 208--
1 2011-xx-x1
1 2011-xx-dh
1 2011-xx-x1
...
Es ist kein Problem im Format meiner Datei . Das Skript cut -c 1-10 file.log | uniq -c
gibt nur gültige Zeitstempel aus. Interessanterweise wird ein Teil der obigen Ausgabe mit cut ... | uniq -c
:
3227382 2011-xx-x9
4474604 2011-xx-x0
5722027 2011-xx-x1
Wir können sehen, dass 4474604
mein anfängliches Skript nach der Anzahl der Unikate fehlgeschlagen ist.
Habe ich ein Limit in Bash erreicht, das ich nicht kenne, habe ich einen Fehler in Bash gefunden (es ist unwahrscheinlich), oder habe ich etwas falsch gemacht?
Update :
Das Problem tritt auf, nachdem 2G der Datei gelesen wurden. Es Nähte read
und Umleitung nicht wie größere Dateien als 2G. Aber immer noch auf der Suche nach einer genaueren Erklärung.
Update2 :
Es sieht definitiv aus wie ein Bug. Es kann reproduziert werden mit:
yes "0123456789abcdefghijklmnopqrs" | head -n 100000000 > file
while read line; do file=${line:0:10}; echo $file; done < file | uniq -c
Aber dies funktioniert gut als Workaround (es scheint, dass ich eine nützliche Verwendung gefunden habe cat
):
cat file | while read line; do file=${line:0:10}; echo $file; done | uniq -c
Ein Fehler wurde bei GNU und Debian gemeldet. Betroffen sind die Versionen bash
4.1.5 unter Debian Squeeze 6.0.2 und 6.0.4.
echo ${BASH_VERSINFO[@]}
4 1 5 1 release x86_64-pc-linux-gnu
Update3:
Dank Andreas Schwab, der schnell auf meinen Fehlerbericht reagiert hat, ist dieser Patch die Lösung für dieses Fehlverhalten. Die betroffene Datei ist, lib/sh/zread.c
wie Gilles früher bemerkte:
diff --git a/lib/sh/zread.c b/lib/sh/zread.c index 0fd1199..3731a41 100644
--- a/lib/sh/zread.c
+++ b/lib/sh/zread.c @@ -161,7 +161,7 @@ zsyncfd (fd)
int fd; { off_t off;
- int r;
+ off_t r;
off = lused - lind; r = 0;
Die r
Variable wird verwendet, um den Rückgabewert von zu halten lseek
. Da lseek
der Offset vom Anfang der Datei zurückgegeben wird, ist der int
Wert negativ , wenn er über 2 GB liegt, was dazu führt, dass der Test if (r >= 0)
dort fehlschlägt, wo er erfolgreich sein sollte.
read
Anweisung in Bash zu zeigen.