Schrift
[thread]8391[/thread]

Wachsenden Dateien folgen: tail -f

Leser: 2


<< |< 1 2 3 4 >| >> 34 Einträge, 4 Seiten
sid burn
 2006-10-06 00:49
#70512 #70512
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
Tag zusammen,
eins vorweg, ich Interessiere mich gerade eher darum ob dies überhaupt machbar ist, und wenn ja wie. Ich habe dafür aber zur Zeit keinen Verwendungszweck, daher kommt es mir jetzt nicht darauf an wieviel Sinn es nun macht soetwas zu Implementieren.

Was ich Wissen würde ist wie man mit "rohen" Perl schafft eine Datei zu Verfolgen. Eine Datei Zeilenweise zu Verfolgen und diese Auszugeben habe ich geschafft und war auch nicht so schwer. Zum anderen gab es dazu ja auch ein Eintrag im perlfaq. Allerdings ist es noch nicht das was "tail -f" macht.

Den Code der dort steht habe ich verändert, zum einen sysread und syswrite genommen. Vor allem syswrite() damit auch Zeilen ausgegeben werden die nicht auf ein Newline Enden. Oder eben nur um Zeichen erweitert werden, und die Zeile noch nicht abgeschlossen ist.

Zusätzlich gibt mir "tail -f" aber immer noch die Daten sofort aus, die ich an einer Datei anhänge. Während man bei der bekannten Methode in perlfaq sein Skript mit "sleep 1" üblicherweise 1 Sekunde warten lässt.

Genau das möchte ich noch vermeiden.

Ich hatte zuerst an select() gedacht, so dass ich erst alles vom Filehandle Lese, und danach solange warte bis auf dem Filehandle wieder etwas zu Lesen vorhanden ist. Da mir select() zu kompliziert ist habe ich IO::Select genommen.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
use IO::Select;

open my $file, '<', 'tail' or die $!;
my $select = IO::Select->new( $file );
while (my @ready = $select->can_read ) {
for my $fh (@ready) {
while ( sysread $fh, my $buff, 100 ) {
syswrite STDOUT, $buff;
}
}
}
close $file;

Zwar Funktioniert das ganze wie erwartet. Allerdings nur auf den ersten Blick. Fügt man ein "syswrite STDOUT, 'a'" in der for Schleife hinzu, direkt vor der while Schleife wird man sehen das dies eine Endlosschleife ist die durchgängig ohne Pause ausgeführt wird.

"top" zeigt dann auch 100% Prozesserauslastung an. Und jetzt meine Frage, warum blockt die Methode "can_read" nicht wie sie sollte?

Zur Zeit ist der Code eher identisch mit
Code: (dl )
1
2
3
4
5
6
7
open my $file, '<', 'tail'  or  die $!;
while (1) {
while ( sysread $file, my $buff, 100 ) {
syswrite STDOUT, $buff;
}
}
close $file;



Gibt es also eine Möglichkeit irgendwie solange zu Blocken bis neue Daten auf ein Filehandle vorhanden sind, und dann erst etwas zu tun? IO::Select funktioniert hier anscheind nicht. Ist wohl eher für Handles gedacht die sich danach Leeren (Sockets), und nicht für Dateihandles.


Noch Interessanter wäre sogar wenn irgendwie ein Event gesendet wird, dass man dann flexibel Abfragen könnte, und nebenbei dann etwas anderes machen kann. Ich glaube aber ohne fork() oder Threads sollte dies wohl unmöglich sein?


Eine andere Frage, warum wird in der FAQ eigentlich gesagt das man clearerr() oder seek() aufrufen soll um das EOF zu Löschen? Bei mir Funktioniert es auch ohne, und auch wenn ich es benutze macht es bei mir keinen Unterschied.


Klar, ich könnte anstatt (sleep 1) auch ein "select( undef, undef, undef, 0.1) nehmen, dann sieht es auch aus, als wenn die Einträge "sofort" erscheinen, und ich habe keine 100% Prozessorauslast, allerdings ist das nicht wirklich die Lösung des Problems, sondern wohl eher nur ein Workaround.


Zu den Modulen:
Habe mir File::Tail angeschaut letztendlich funktioniert es anscheind auch nur so das es "sleep" aufruft.\n\n

<!--EDIT|sid burn|1160081851-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
esskar
 2006-10-06 01:20
#70513 #70513
User since
2003-08-04
7321 Artikel
ModeratorIn

user image
unter windows funzt dein script kein stück.
es word nichtmal die datei ausgegeben.
ansonsten könnte man die windows api anprogrammieren und die Function FindFirstChangeNotification anprogrammieren ( gibts vielleicht auch schon auf cpan, hab es nur nicht gefunden ).

für linux hab ich noch CPAN:Linux::Inotify2 gefunden.
betterworld
 2006-10-06 01:59
#70514 #70514
User since
2003-08-21
2614 Artikel
ModeratorIn

user image
[quote=sid burn,05.10.2006, 22:49]Zusätzlich gibt mir "tail -f" aber immer noch die Daten sofort aus, die ich an einer Datei anhänge.[/quote]
Das stimmt nicht. Ueber strace findet man raus, dass tail -f auch immer eine Sekunde wartet. Mit bloßem Auge faellt das aber kaum auf, weil die Reaktionsgeschwindigkeit nicht so schnell ist, außerdem hat man nur selten Vergleichsgelegenheit.
ptk
 2006-10-06 02:20
#70515 #70515
User since
2003-11-28
3645 Artikel
ModeratorIn
[default_avatar]
Ich hätte auch gedacht, dass ein select() bei einem EOF wartet, aber dem ist nicht so. Ein Aufruf deines Skriptes mit truss zeigt:
Code: (dl )
1
2
select(8,{3},0x0,0x0,0x0)                        = 1 (0x1)
read(0x3,0x8185a88,0x64) = 0 (0x0)

in einer Endlosschleife laufen, also liefert select() immer sofort ein lesbaren Filehandle zurück. Und es scheint auch so gewollt zu sein, siehe beispielsweise den Thread hier: http://sources.redhat.com/ml/glibc-bugs/2004-05/msg00024.html

Bei FreeBSD sieht ein echtes "tail -f" in truss so aus:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
fstat(3,0xbfbfdde0)                              = 0 (0x0)
break(0x8050000) = 0 (0x0)
lseek(3,0x0,SEEK_CUR) = 0 (0x0)
lseek(3,0x0,SEEK_SET) = 0 (0x0)
read(0x3,0x804f000,0x1000) = 487 (0x1e7)
munmap(0x28159000,0x1e7) = 0 (0x0)
read(0x3,0x804f000,0x1000) = 0 (0x0)
kqueue() = 4 (0x4)
fstatfs(0x3,0xbfbfde20) = 0 (0x0)
kevent(0x4,0x804d080,0x1,0x0,0x0,0xbfbfde18) = 0 (0x0)


Und das Programm wartet. Hier gibt es also einen speziellen "kernel event notification mechanism". Linux hat bestimmt etwas Ähnliches, oder verwendet eben auch nur den sleep 0.1-Workaround. Schau mal mit strace rein.
ptk
 2006-10-06 02:21
#70516 #70516
User since
2003-11-28
3645 Artikel
ModeratorIn
[default_avatar]
Der Vollständigkeit halber: unter Solaris 10 wird in einer Schleife gewartet:
Code: (dl )
1
2
3
4
nanosleep(0xFFBFF188, 0xFFBFF180)               = 0
read(0, 0x00024718, 65537) = 0
nanosleep(0xFFBFF188, 0xFFBFF180) = 0
read(0, 0x00024718, 65537) = 0
sid burn
 2006-10-06 02:21
#70517 #70517
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
[quote=betterworld,05.Oct..2006, 23:59][quote=sid burn,05.10.2006, 22:49]Zusätzlich gibt mir "tail -f" aber immer noch die Daten sofort aus, die ich an einer Datei anhänge.[/quote]
Das stimmt nicht. Ueber strace findet man raus, dass tail -f auch immer eine Sekunde wartet. Mit bloßem Auge faellt das aber kaum auf, weil die Reaktionsgeschwindigkeit nicht so schnell ist, außerdem hat man nur selten Vergleichsgelegenheit.[/quote]
Jau, stimmt!

Und doch, dass kann man sogar recht gut testen. Einfach zwei Terminal Emulatoren öffnen. Den einen mit "tail -f datei" füttern und in der anderen Datei "echo -n "blablub" >> datei" eintippen. Dann sieht man auch die Verzögerung bei "tail". Eine Sekunde Reaktionszeit bekommt man ohne Probleme mit.

Komischerweise kamm mir das "tail" immer so vor als wenn es "sofort" senden würde.

Naja, dann eben umändern des Titels. Ein besseres "tail -f" in Perl. ;)



Quote
unter windows funzt dein script kein stück.
es word nichtmal die datei ausgegeben.

Wow. Auch nicht vom zweiten Code Schnipsel, wo nur sysread() und syswrite() vorkommt?

Ja Inotify ist mir bekannt, dachte aber es gäbe da eine Portable Lösung.

Wahrscheinlich so wie es sich anhört muss man wirklich den Kernel nach solchen Informationen anfragen.

Quote
Und das Programm wartet. Hier gibt es also einen speziellen "kernel event notification mechanism". Linux hat bestimmt etwas Ähnliches, oder verwendet eben auch nur den sleep 0.1-Workaround. Schau mal mit strace rein.

Ist so wie betterworld sagte, es wartet in einer Schleife immer 1 Sek.
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
write(1, "foofoofoofoobarbarbarbartesttest"..., 75foofoofoofoobarbarbarbartesttesttesttesttesttestblubblubhkjhblablablablabla) = 75
nanosleep({1, 0}, NULL) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=208, ...}) = 0
nanosleep({1, 0}, NULL) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=208, ...}) = 0
nanosleep({1, 0}, NULL) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=208, ...}) = 0
nanosleep({1, 0}, NULL) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=208, ...}) = 0
nanosleep({1, 0}, NULL) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=208, ...}) = 0
nanosleep({1, 0}, NULL) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=208, ...}) = 0
nanosleep({1, 0}, <unfinished ...>
\n\n

<!--EDIT|sid burn|1160087082-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
betterworld
 2006-10-06 02:27
#70518 #70518
User since
2003-08-21
2614 Artikel
ModeratorIn

user image
Du kannst es ja so machen, dass Du inotify nimmst, wenn es verfuegbar ist, ansonsten sleep. Wenn man das Intervall auf ein paar Hundertelsekunden verkuerzt, duerfte die Prozessorauslastung das eigentlich auch nicht so stoeren.
betterworld
 2006-10-06 02:37
#70519 #70519
User since
2003-08-21
2614 Artikel
ModeratorIn

user image
[quote=ptk,06.10.2006, 00:20]Ich hätte auch gedacht, dass ein select() bei einem EOF wartet, aber dem ist nicht so.[/quote]
In vielen Faellen moechte man ja auch informiert werden, wenn die Datei zu Ende geht (oder die Verbindung/Pipe/etc. geschlossen wird). Es waere ja fuerchterlich, wenn alle Dateideskriptoren bis in alle Ewigkeiten im select() rumbaumeln, obwohl sie gar nichts mehr bringen.

Eigentlich ist select() ja sowieso nicht fuer regulaere Dateien konzipiert worden.\n\n

<!--EDIT|betterworld|1160087995-->
sid burn
 2006-10-06 02:43
#70520 #70520
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
[quote=betterworld,06.Oct..2006, 00:37][quote=ptk,06.10.2006, 00:20]Ich hätte auch gedacht, dass ein select() bei einem EOF wartet, aber dem ist nicht so.[/quote]
In vielen Faellen moechte man ja auch informiert werden, wenn die Datei zu Ende geht (oder die Verbindung/Pipe/etc. geschlossen wird). Es waere ja fuerchterlich, wenn alle Dateideskriptoren bis in alle Ewigkeiten im select() rumbaumeln, obwohl sie gar nichts mehr bringen.

Eigentlich ist select() ja sowieso nicht fuer regulaere Dateien konzipiert worden.[/quote]
Hmm,
ich verstehe das irgendwie etwas anders.

Ich führe ja die Methode can_read() extra aus, und erwarte ja gerade eben, dass er solange wartet bis wieder etwas verfügbar ist. Sollte ja auch Sinn der Sache sein.

Wenn man dies auf ein Socket macht, dann wartet man solange bis sich irgendjemand verbindet, und man Daten gesendet bekommt. Wenn man keine Anfragen bekommt, z.B. bei einem Server dann muss dieser ja auch nichts Antworten.

Von daher hätte ich eigentlich erwartet das es solange Blockt bis etwas vorhanden ist. Ein "$input = <STDIN>" blockt ja auch so lange bis man etwas eingibt.
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
Strat
 2006-10-06 12:42
#70521 #70521
User since
2003-08-04
5246 Artikel
ModeratorIn
[Homepage] [default_avatar]
um moeglichst portabel zu bleiben, verwende ich dafuer gerne die folgende Loesung (mit sleep)
Code: (dl )
1
2
3
4
5
6
7
8
9
open( my $FH, "<", $filename )
or die "Error: couldn't open file '$filename': $!\n";
while( 1 ) {
while( my $line = <$FH> ) {
print $line;
}
sleep 1;
seek( $FH, 0, 1 );
} # while 1

oder mit IO::Handle:

Code: (dl )
1
2
3
4
5
6
7
while( 1 ) {
while( my $line = <$FH> ) {
print $line;
}
sleep 1;
$FH->clearerr();
} # while 1


oder ich wuerde mir mal die Implementierung von CPAN:File::Tail ansehen...
perl -le "s::*erlco'unaty.'.dk':e,y;*kn:ai;penmic;;print"
http://www.fabiani.net/
<< |< 1 2 3 4 >| >> 34 Einträge, 4 Seiten



View all threads created 2006-10-06 00:49.