Ext. Programm ausführen und Standardausgabe speichern ?



  • Hi!

    Ich sag´ lieber gleich vorneweg, dass ich hoffe, dass ich mich verständlich ausdrücke... ;o)
    Ich muss ab und an supereinfache Sachen in C machen. Daher habe ich nur ein beschränktes autodidaktisches Wissen.

    Und zwar habe ich ein Programm, das auf meiner Unix-Konsole einen Ausgabetext liefert.
    Dieses Programm würde ich nun gerne bei mir einbinden und die Ausgabe verfügbar haben.
    Ausführen kann ich es ja, aber wie belege ich die Variable ohne dass ich die Ausgabe in eine Datei zwischenspeichere, die ich dann wieder lesen muss?

    (...)
    printf("Programm wird ausgeführt...\n");
    system("exprog");
    printf("Standardausgabe= %s",variable)
    (...)

    Danke sagt KaraokeGirl...

    PS: Crosspost zu spotlight.de



  • Hallo, was für ein UN*X? Normalerweise geht das über popen() (oder allgemein Pipes), man bekommt ein Filehandle, das man lesen kann. Unter Solaris gibt's dann sogar noch das hübsche p2open(), das sogar Infos ans externe Programm schicken kann. Sowas kann man aber unter allen Unices basteln. Da müsst ich aber kurz nachgucken.
    Reicht die Info schon oder muss ich wühlen gehen?



  • Ich hab mal schon im Voraus etwas gewühlt und das was ich gefunden hab, entspringt einem früheren Spotlight Beitrag von mir.
    So bastelt man ungefähr ein p2open() unter Linux (alter Beitrag aus Spotlight):

    Hallo,

    Deine Idee stimmt nicht ganz, weil das entspricht ja nicht der Aufgabenstellung. Deine Ausgabe geht ja nicht mehr an den aufrufenden Prozess zurueck, sondern in ne Datei. Wenn ich Dich vorher richtig verstanden habe, dann soll aber die Kommunikation bidirektional sein. Ein popen() macht aber nur eine Pipe auf, womit das also mit der Bidirektionalitaet nix wird.
    Auf das aufgerufene Programm hast Du aber anscheinend wenig Einfluss und musst ihm nun ein ganz normales stdin, stdout und stderr vorgaukeln. Ein popen() kann das nur sehr unvollstaendig.
    Das Prob koennte man sowohl mit UNIX Domain Sockets als auch mit Pipes loesen. Ich neige zu zweiterem.
    Die Idee ist einfach. Der aufrufende Prozess macht (mindestens) zwei Pipes auf, eine zum lauschen, die andere zum sprechen und macht dann ein fork().
    Der Parentprozess wartet nun auf die Kommunikation ueber die Pipes.
    Der Childprozess hingegen schliesst erst mal die benutzten Standarddescriptoren 0 und 1 ggf. auch 2 und verbiegt die fuer ihn sichtbaren Enden der Pipes auf die Standardkanaele.
    Anschliessend verabschiedet sich der Childprozess mit Hilfe von execlp() und wird zum gewuenschten Befehl.
    In Bej's Guide findet sich dazu ein ganz nettes Beispiel. Ich hab das mal etwas modifiziert, so dass es eher auf auf Dein Problem anzuwenden ist.
    Dieses kleine Hau-Ruck Prograemmchen tut nix anderes, als wc aufzurufen, dem einen String zu schicken, das Ergebnis engegenzunehmen und dann auszugeben (guck mal Mama, ganz ohne Files *g*). Klar ist's ein doofes Beispiel, aber ich denke damit wird einiges klarer. Auf Error-Checks hab ich der Uebersichtlichkeit wegen verzichet. Es werden auch nur stdin und stdout umgebogen - aber egal, so ist's uebersichtlicher:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main()
    {
      char buf[128];
    
      int pfds0[2], pfds1[2];
      FILE *fp_in, *fp_out;
    
      pipe(pfds0);
      pipe(pfds1);
    
      if (!fork()) {        /* child */
        close(0);           /* close normal stdin */
        dup(pfds1[0]);      /* make stdin same as pfds1[0] */
        close(1);           /* close normal stdout */
        dup(pfds0[1]);      /* make stdout same as pfds0[1] */
        close(pfds0[0]);    /* we don't need this end of the pipe */
        close(pfds1[1]);    /* we don't need this end of the pipe */
        execlp("wc", "wc", NULL);
      } else {              /* parent */
        fp_in  = fdopen(pfds0[0], "r");
        close(pfds0[1]);    /* we don't need this end of the pipe */
        fp_out = fdopen(pfds1[1], "w");
        close(pfds1[0]);    /* we don't need this end of the pipe */
        fputs("Hello, this is the world of pipes!", fp_out);
        fclose(fp_out);
        while(fgets(buf, sizeof(buf), fp_in)) printf("answer: %s", buf);
        fclose(fp_in);
        wait(NULL);         /* no zombies please :-) */
      }
      return 0;
    }
    

    Die Sache ist halt etwas verwirrend, weil man mit den zu benutzenden Enden der Pipes leicht durcheinander kommt.
    Aber isses nicht schoen? Unter UN*X ist alles ein File - und deswegen duerfen wir auch fdopen() mit ner Pipe machen 🙂 IPC und UN*X sind geil!!!!

    Viele Gruesse,

    yacc

    PS:

    Um beim Protokoll (wer sagt wann was und wer hoert wann zu) nicht durcheinander zu kommen, kann man prima select() missbrauchen! Es koennt ja sein, das der aufgerufene Prozess aus irgend einem Grund losplappert, obohl das nicht erwartet wird.

    PS: Falsches Forum! Das gehört in die Unices-Area!



  • Und hier nochmal die Originalquelle:

    http://www.ecst.csuchico.edu/~beej/guide/ipc/



  • Also zunächst mal: Das System ist Solaris 8
    Ich tendiere dazu, dass das eigentlich genau das ist, was ich meinte (nur dass es mir vielleicht doch ein bisschen hoch ist).

    Ich hab´ mir übrigens in der Zwischenzeit eine Lösung mit Files gebastelt (für mich gar nicht so einfach)...
    -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

    system("echo \"Hallo Welt\" > tmpfile");
            FILE *ftmp;
            char chrtmp[1]="";
            char chrout[50]="";
    	int c;
    	ftmp = fopen("tmpfile","r");
    	while ((c = fgetc(ftmp)) != EOF)
    	{
    		sprintf(chrtmp,"%c",c);
    		if(c!='\n') {strcat(chrout , chrtmp); };
    	}
    	printf(">%s<",chrout);
    	fclose(ftmp);
    

    -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

    Ich hatte eigentlich gehofft, dass das schon etwas einfacher zu bewerkstelligen ist - ich befürchte, da muss ich bei meiner umständlichen aber für mich verständlichen Vorgehensweise bleiben... ;o(



  • PS: Verbesserungsvorschläge natürlich weiterhin jederzeit willkommen... 😉



  • wie someone schon angedeutet hat, könntest du mit man: popen() eine Pipe öffnen (anstelle von system()) und die Ausgaben des aufgerufenen Programms in einen eigenen FILE* umleiten:

    FILE* pdata = popen("echo \"Hallo Welt\"","r");
    char out[51];
    while(fgets(out,50,pdata)!=NULL)
      printf(">%s<",out);
    pclose(pdata);
    

    (btw, dein Umgang mit dem Speicher ist imho bedenklich)



  • Wow! Das gefällt mir supergut! 🙂 Genau was ich brauche.
    Da sag´ ich schon mal danke für...

    Inwiefern findest Du das bedenklich? (und mit dieser Frage will ich bestimmt nicht abstreiten, dass es das nicht ist 😉 😉 )



  • Du hast ein 50 Zeichen großes Array angelegt und vergisst dann zu überprüfen, weiviel du schon eingelesen hast. Und das Array char chrtmp[1]=""; ist aus zu klein, um ein Zeichen UND den Null-Terminator unterzubringen - mit dem Ergebnis, daß die strcat()-Anwendung undefiniertes Verhalten liefert.



  • OK, allerbesten Dank noch einmal!
    Mein Tag ist gerettet! 👍



  • Darf ich vielleicht doch noch einmal aufdringlicher Weise eine kleine Frage dazu stellen?

    In "out" habe ich jetzt einen string aus 4 Zeichen stehen - z.B. "a1cd".
    Wie kann ich denn feststellen, ob der zweite Character eine "1" oder eine "0" ist?

    Bisher habe ich sowas immer mit strtok gemacht, aber da hatte ich auch vernünftige Trennzeichen...



  • Im einfachsten Fall vergleichst du die Zeichen mit deinen Wunschwerten (an die einzelnen Elemente des Strings kommst du über Index-Zugriff):

    if(out[1]=='1')//Die Zählung beginnt bei 0, deshalb ist out[1] das zweite Zeichen
      printf("1 gefunden");
    


  • 😃 Nochmal danke - und jetzt halte mich wieder etwas zurück... 😉



  • Auch von mir ein riesen Dank :D.
    Hat auch meinen Tag gerettet.



  • der thread ist zwar schon ziemlich alt, aber ich habe da noch eine frage zu.
    Wie kann ich bei dem befehl execlp sagen, in welchem verzeichnis der befehl ausgeführt werden soll?
    und wie kann ich das codebeispiel mit den 2 threads in windows umsetzen?

    danke
    scosu



  • scosu schrieb:

    der thread ist zwar schon ziemlich alt, aber ich habe da noch eine frage zu.
    Wie kann ich bei dem befehl execlp sagen, in welchem verzeichnis der befehl ausgeführt werden soll?

    Indem du den kompletten Pfad übergibst.

    scosu schrieb:

    und wie kann ich das codebeispiel mit den 2 threads in windows umsetzen?

    danke
    scosu

    Indem du WinApi Funktionen verwedest, die einen Thread starten ( CreateThread ).



  • danke für deine antwort

    wie läuft das denn mit den pipes, dem lesen von der pipe und das umbiegen des standardoutputs?

    also habe hier eine server binary und die öffnet relativ zu dem ausführungspfad daten die sie braucht. aber ich weiß nicht wie man es schafft, dass die datei in dem gleichen ordner ausgeführt werden, sodass auch die relativen pfadangaben passen.


Log in to reply