Lesen aus stderr via pipe Problem
-
Hallo!
Ich nutze für die Kommunikation mit Kommandozeilenprogrammen popenRWE, stdout/stderr auszulesen. Mein Code zum auslesen schaut wie folgt aus:
... popenRWE ... char inBuf[8192]; memset(inBuf, '\0', sizeof(inBuf)); size_t inRead; // pipe[1] = stdout, pipe[2] = stderr while((inRead = read(pipe[2], inBuf, sizeof(inBuf))) > 0) { printf("%s\n", inBuf); memset(inBuf, '\0', sizeof(inBuf)); } ... pclose RWE ...
Während das Auslesen von stdout ganz normal funktioniert, hackt es beim Auslesen von stderr gewaltig. Habe dazu mal ein kleines Testprogramm geschrieben:
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <strings.h> void std_out(int x) { for(int i = 0; i < x; i++) { printf("-"); } printf("\n"); } void std_err(int x) { for(int i = 0; i < x; i++) { fputs("*", stderr); } fputs("\n", stderr); } int main(int argc, char **argv) { for(int i = 0; i < 123; i++) std_err(i); // Return return 0; }
Dieses habe ich einmal für std_out und einmal für std_err kompiliert. Beim Auslesen von stdout erhalte ich ordnungsgemäß folgenden Output:
--
---
----
-----
------
...Beim Auslesen von stderr hingegen erhalte ich sowas wie:
**
*
**
****
****
*
******
...Es wird also mit jedem Durchlauf eine beliebige Anzahl von Zeichen ausgelesen und dann ausgegeben. Und das beste: Die Ausgabe ist pro Durchlauf unterschiedlich, als wäre da irgendwo ein Zufallsgenerator hintergeklemmt der bei jedem Aufruf von read mal eben spontan entscheidet wieviele Zeichen eingelesen werden.
Hat jemand von Euch evtl. eine Idee womit das zusammenhängen könnte? Habe das jetzt auf zwei Linuxrechnern getestet (beides Ubuntu), mit jeweils selben resultat.
Danke schonmal im voraus.
-
stderr ist im Gegensatz zu stdout ungepuffert, d.h. beim Auslesen erhältst du immer so schnell wie möglich die Daten.
Schreibe mal am besten einfach nochfprintf(stderr, "%d", x);
raus, dann siehst du das besser...
PS: Dass du beim Auslesen aus stdout immer zeilenweise die Ergebnisse erhältst, ist aber implementationsabhängig (darauf verlassen kannst du dich nicht, sondern das ist eher als Zufall anzusehen).
Gib am besten daher die Daten 1:1 beim Auslesen wieder (also ohne den zusätzlichen Zeilenumbruch):printf("%s", inBuf); // bzw. puts(inBuf);
-
Th69 schrieb:
Gib am besten daher die Daten 1:1 beim Auslesen wieder (also ohne den zusätzlichen Zeilenumbruch):
printf("%s", inBuf); // bzw. puts(inBuf);
puts
hängt (im Gegensatz zufputs
) ein '\n' an die Ausgabe.
-
Hallo,
danke für die Aufklärung. Das bringt mich jetzt allerdings vor ein Problem: Einige Programme wie unrar z.B. nutzen gerne mal Backspace oder auch Zeilenvorschübe in der Ausgabe. Gerade da bin ich aber darauf angewiesen den genauen Anfang einer Zeile zu erkennen, um entsprechend auf die Meldungen reagieren zu können. Ich hab nun versucht den Backspace durch \n und die Zeilenvorschübe durch ein Leerzeichen zu ersetzen mit dem Resultat, dass ich nun gar nichts mehr (richtig) angezeigt bekomme.
Wie kann ich den stderr Stream nun am besten handlen bzw. kann ich den irgendwie dazu bringen sich genauso wie stdout zu verhalten?
-
Versuchst du gerade Programme fernzusteuern?
-
Sowas in der Art.
-
C-Quence schrieb:
Sowas in der Art.
Da gibt es sicher wesentlich bessere Methoden. Diese Nutzerinterfaces die du da anscheinend gerade "einlesen" möchtest sind meistens bereits Programme wie deines, die über dem eigentlichen Konsolenprogramm laufen. Oder wenn alles in einem integriert ist, dann haben Programme dieser Art auch meistens einen Schalter, der den Text-GUI-Modus abschaltet, so dass das Programm keine Steuerzeichen ausgibt, sondern nur noch einfachen Text.
-
Tja, diesen scheint es bei "unrar" nicht zu geben. Was ich aus der man. bisher herausgelesen hab ist:
-c-: Disable comments show (was macht das ???)
-inull: Gibt gar nichts mehr aus> unrar hat ne Option -vp, die nach jedem Teilarchiv stoppt und den Benutzer fragt ob es weitermachen soll. Mit -inull würd ich hier die Aufforderung nicht mehr mitkriegen.
- Disable messages. c-copyright, d-'done' message, p-centage, q-be completely quiet
-id[c,d,p,q]: Damit kann man wohl teilmessages ausblenden.
> -idq würde alles bis auf Error-Messages und Questionstrings ausblenden. Bringt mir aber auch nichts, da ich a. die anderen Informationen ja auch einlesen will und b. diese ja trotzdem über stderr kommen.Für das deaktivieren von Steuerzeichen habe ich bisher nichts finden können. Aber gerade unrar wäre ein Kommandozeilenprogramm welches mir wichtig wäre es anzubinden, ist soweit ich weiß aber auch nicht open source.
-
Ok, einen dummen Fehler habe ich ausgemerzt. Mittlerweile gehe ich nun wie folgt vor:
for(size_t i = 0; i < strlen(inBuf); i++) { if(inBuf[i] > 0 && inBuf[i] > 31) { outBuf[outRead++] = inBuf[i]; } } outRead = 0;
Hatte vorher vergessen dass 0 = \0 entspricht, weswegen es zu Problemen kam. Nun bekomme ich letztendlich die komplette Ausgabe als reinen Text, den ich in ein Char konkateniere und anschließend manuell Zeilenumbrüche einfüge, wodurch ich die Ausgabe nun wieder Zeile für Zeile bekomm.
-
@DirkB: stimmt, das vergesse ich immer wieder.
Also:printf("%s", inBuf); // bzw. fputs(inBuf, stdout);