popen

G

genius0815

Grünschnabel
Hallo ich versuche/suche eine Funktion, die ein bashcommando ausführt und dann den Wert zurückgibt. Mit system erhalte ich nur den Fehlerwert zurück. Da hab ich popen gefunden, aber ich krieg es nicht hin:

cmd ist ein beliebiges Kommande der shell, der Rückgabewert soll ein Zeiger auf einen Char sein. Man weiss vorher nicht wie gross die Ausgabe des Kommandos ist. Den Rückgabewert will ich dann mit printf ausdrucken lassen beim Aufruf der Funktion.
Ich glaub das was ich hier zusammmen gebaut hab, ist völlig falsch, aber ich weiss es gerade nicht besser und hab die anderen Varianten schon gelöscht.

Code:
char read_cmd(char *cmd)
{
		const size_t s = 1024;
        size_t bytes=8;
        char buffer[s];
		char *output;
 		FILE* fp = NULL;
		int nRet;
		size_t *t = malloc(0);

		char **gptr = (char **)malloc(sizeof(char*));
		*gptr = NULL;

		if((fp = popen(cmd,"r")) == NULL){
                      printf ("Failure during read command output\n");
                }
		else {
			fread( buffer, bytes, s, fp);
			gptr = (char *)buffer;
			**gptr = realloc(gptr, s);
			return gptr;
		}
}

Danke für Hilfe
 
Ohne genau dabei durchgestiegen zu sein was Du da gecodet hast erklär Ich dir mal wie man sowas prinzipiell macht (Ich mach das so selten in C, daher weiß ich nicht immer die genauen parameter etc):

Du öffnest mit popen eine pipe zu deinem Programm, das hast Du ja schon richtig gemacht.
Dann würde Ich einen Puffer anlegen der, weiß Ich, 100 byte groß ist. Dann solltest Du so lange bis zu 100B lesen bis der Stream geschlossen wird. Nach jedem lesevorgang solltest Du den Puffer mit realloc neu belegen, das geht mit realloc recht gut. Du musst nur aufpassen dass jedesmal ein neuer pointer an fread übergeben wird, weil Du ja nicht jedesmal den Puffer von vorne wieder zuschreiben willst. Achte auch darauf wie viel wirklich gelesen wurd, wobei das vermutlich nicht relevant ist, da Streams standardmäßig blockieren. Dies ist glaube Ich nur bei TCP problematisch. Du musst also jedesmal bisher_gelesene_bytes + letztes_mal_gelesene_bytes + 100B Speicher anfordern. Am Ende jagst Du den string am besten durch strdup, der macht eine Kopie was den vorteil hat dass Du nicht den überschüssigen speicher selbst freigeben musst. Du machst am Ende folgendes:

/* buffer_all ist der puffer in dem alles am ende drinsteht, plus eventuell überschüssiger Speicher */
char *ret_buffer = strdup(buffer_all);
free(buffer_all);
return ret_buffer;

Damit sollte es dann erledigt sein.

PS: die äußerst umständliche Speicherverwaltung ist der Grund dafür warum Ich lieber Objektorientiert Programmiere.

PPS: vielleicht gibt es in C auch eine Funktion die 2 strings aneinander hängt. Dann könntest Du auch einfach vor jedem lesevorgang aus der pipe einen 100B puffer anlegen, 99B lesen (NULL am ende nicht vergessen), und den an das bisher gelesene Anhängen. Das würde Dir eine Menge Arbeit abnehmen.

Ich hoffe Ich hab Dir mehr geholfen als dass Ich Dich verwirrt habe.
 
wobei strcat() afaik den speicher nicht selbst reserviert, wobei das ja dank strlen() kein großes Problem darstellt.
 
O Dann solltest Du so lange bis zu 100B lesen bis der Stream geschlossen wird. Nach jedem lesevorgang solltest Du den Puffer mit realloc neu belegen, das geht mit realloc recht gut. Du musst nur aufpassen dass jedesmal ein neuer pointer an fread übergeben wird, weil Du ja nicht jedesmal den Puffer von vorne wieder zuschreiben willst. Achte auch darauf wie viel wirklich gelesen wurd, wobei das vermutlich nicht relevant ist, da Streams standardmäßig blockieren. Dies ist glaube Ich nur bei TCP problematisch. Du musst also jedesmal bisher_gelesene_bytes + letztes_mal_gelesene_bytes + 100B

I.

Genau diesen Vorgang krieg ich nicht hin. Kann jemand mir etwas Quelltext als Anhaltspunkt geben?

gruss
 
Schon mal als Warnung: Ich habe diesen code nicht getestet, Ich hab ihn einfach mal so wie Ich denke dass es laufen _könnte_ runtergeschrieben:

Code:
char *read_cmd(char *cmd) {
#define BUFSIZE 100
   char *tmp_buf = (char *)malloc(BUFSIZE + 1);
   /* vielleicht ist das +1 nicht nötig, aber es kann nicht schaden */
   char *ret_buf = (char *)malloc(1); /* für realloc nötig */
   int bytes_read = 0;
   int bytes_saved = 0;
   *tmp_buf = *ret_buf = '\0'; /* als string markieren */
   FILE *pipefd = popen(cmd, "r"); /* programm starten */
   if (!pipefd) {
      fprintf(stderr, "Failure starting command\n")
      exit(1);
   }
   while(!feof(pipefd)) { 
      /* dies nur so lange machen wie es etwas zu lesen gibt */
      bytes_read = fread(tmp_buf, sizeof(char), BUFSIZE, pipefd);
      ret_buf = realloc(ret_buf, bytes_saved + bytes_read + 1); 
      /* die adresse kann sich ändern */
      strncpy(ret_buf, tmp_buf, bytes_read);
      bytes_saved += bytes_read;
   }
   free(tmp_buf);
   return ret_buf;
}

Also wie gesagt, das Ganze ohne Gewähr!
 
Hallo,

es funktioniert leider nicht ganz so wie gewünscht. Das Problem ist, dass mehr zeilige Ausgaben des Commandos abgeschnitten werden. Muss ich irgendwo noch ein Linefeed einbauen? Oder das ganze mit getline machen?

Hab jetzt rausgefunden, dass die Länge des zurückgegebenen Strings von BUFSIZE abhängt. Wird das doch nicht aneinander kopiert? Wenn ich das richtig verstehe ist doch ret_buf der Rückgabewert?

gruss

Jetzt hab ichs: statt strncpy muss man strncat nehmen zum aneinanderreihen der Strings sonst überschreibt er das alte.
 
Zuletzt bearbeitet:
Oh, das wollte Ich eigentlich auch benutzen, muss mich wohl vertan haben.
Aber dann gehts?
 
Oh, das wollte Ich eigentlich auch benutzen, muss mich wohl vertan haben.
Aber dann gehts?

Ja jetzt funktionert es danke.
Wollte es erst noch umbauen auf eine verkettete Liste, weilich mit den strings hinterher was anfangen wollte, aber krieg ich net hin,muss ich wohl dann auf der shellebene machen.

gruss
 

Ähnliche Themen

freed liefert 0

wirre zeichen :(

fußballstatistik auswerten

Aus Datei lesen

incompatible types in assignment

Zurück
Oben