bash befehle in c - pipe() + fork()

Shadow

Mitglied
Hallo !

Ich müsste ein C Programm schreiben mit dem man eine unbeschränkte Anzahl von Bash-Befehlen verarbeiten kann.
Beispiel: myBashPipe "cat /etc/passwd" "grep root" "cut -d : -f 7" "wc -m"
soll dasselbe Ergebnis wie cat /etc/passwd | grep root | cut -d : -f 7 | wc -m liefern


Das Problem ist dass ich im Umgang mit Pipes und Forks noch nicht sehr geübt bin...
Ein Programm dass nur 2 "BEFEHLE" verarbeiten kann ist kein Problem:


Aber wie verwalte ich eine beliebige Anzahl von Pipes für eine beliebige Anzahl von Befehlen Irgendwie rekursiv ? Habe irgendwo was von "benannten Pipes" gelesen, aber ich muss ohne diese auskommen.
Ich hoffe mir kann jemand helfen.
mfg
Shadow
 
Zuletzt bearbeitet:
Shadow hat gesagt.:
Hallo !

Ich müsste ein C Programm schreiben mit dem man eine unbeschränkte Anzahl von Bash-Befehlen verarbeiten kann.
Beispiel: myBashPipe "cat /etc/passwd" "grep root" "cut -d : -f 7" "wc -m"
soll dasselbe Ergebnis wie cat /etc/passwd | grep root | cut -d : -f 7 | wc -m liefern


Das Problem ist dass ich im Umgang mit Pipes und Forks noch nicht sehr geübt bin...
Ein Programm dass nur 2 "BEFEHLE" verarbeiten kann ist kein Problem:
Code:
/**
* > cat /etc/passwd | grep root
* ergibt:
* 	root:x:0:0:root:/root:/bin/bash
*/
 
#include <stdio.h>
#include <string.h> 	/* strcpy,strcmp */
#include <stdlib.h> 	/* malloc,exit,free */
#include <fcntl.h> 	/* forks	*/
#include <unistd.h> 	/* STDOUT_FILENO, STDIN_FILENO*/
 
int iPid = 0;
int fileDescriptor[2];
 
 
int main (int argc, char** argv) {
 
	if ( argc != 3 ) {
		printf("Too few parameters. Program need 3.\n");
		exit(-1);
	}
 
	if(pipe(fileDescriptor)==-1) {
		printf("Error while creating pipe for fileDescriptor.");
		exit(-1);
	}
 
	char *pcCommand = strtok(argv[1], " ");
	char *pcFile = strtok(NULL, " ");
 
 
	char *pcCommand2 = strtok(argv[2], " ");
	char *pcFile2 = strtok(NULL, " ");
 
	if((iPid=fork())==-1) {
		printf("Error while doublicating processes.");
		exit(-1);
	}
 
	if (iPid == 0) { // Hier handelt es sich um das Kind
		dup2(fileDescriptor[1], STDOUT_FILENO);
		close(fileDescriptor[0]);
		close(fileDescriptor[1]);	
		// execlp("cat", "cat", "/etc/passwd", NULL);
		execlp(pcCommand, pcCommand, pcFile, NULL); 				exit(-1);
	}
	else {
		dup2(fileDescriptor[0], STDIN_FILENO);
		close(fileDescriptor[0]);	
		close(fileDescriptor[1]);	
		// execlp("grep", "grep", "root", NULL);
		execlp(pcCommand2, pcCommand2, pcFile2, NULL);.
		wait(&iPid);
	}
}

Aber wie verwalte ich eine beliebige Anzahl von Pipes für eine beliebige Anzahl von Befehlen Irgendwie rekursiv ? Habe irgendwo was von "benannten Pipes" gelesen, aber ich muss ohne diese auskommen.
Ich hoffe mir kann jemand helfen.
mfg
Shadow

Mal ganz davon abgesehen das man dein Problem mit unendlich vielen Eingaben auch ohne Paralleles Arbeiten (fork und co) machen kann, hast du das doch in deinem Quellcode schon unterschieden, und zwar anhand der iPID. Wenn du nun willst das z.b. dein Child Prozess ebenfalls Vater spielt, dann kannste darin wieder Forken, und dann haste wieder nen neuen Prozess, bis irgendwann mal kein Speicher mehr da ist ;)

Du kannst das beliebig ineinander Schachteln. Wenn du jedoch willst das die Prozesse, z.b. Vater und Kind miteinander Kommunizieren können sollen, musste nen Tunnel aufbauen, worüber die sich Daten schicken können.

Sollte das deine Frage nicht beantworten, stell die Frage bitte nochmal anders.

Gruss

MFC OpenGL
 
hallo !
nein die frage ist nicht beantwortet :)

der sinn und zweck von den forks und pipes ist die uebung. Mir ist schon klar dass ein ich mit jeder anderen formulierung des programmes besser aussteige :)

Der wirkliche Sinn diese Programmes ist, dass ich einen Vater habe ein LEBENDES Kind.
das Kind führt den ersten Befehl aus und schreibt das Ergebnis in die Pipe. Das nächste Kind holt dann den Befehl aus der Pipe raus und führt dessen, zugeilten, Befehl aus und schreibt diesen wieder in die Pipe.... Dies geht solange bis man zum letzten Befehl kommt -> der Vater holt sich das bisherige Ergebnis aus der Pipe und führt den letzten Befehl durch....
Die Frage ist nun so zu stellen: Wie gewährleiste ich einen unendlich langen Bash Befehl?
Hoffe die Frage ist nun etwas klarer :)
Wie gesagt, mit 2 Commands geht es bereits - siehe ersten Beitrag...


Im Anhang ein Beispiel :)

mfg
 

Anhänge

  • bsp.JPG
    bsp.JPG
    15 KB · Aufrufe: 379
Zuletzt bearbeitet:
Ok, jetzt ist mir klar was du machen willst ;)

Also, zunächst musst du dafür Sorgen das der Pappi brav darauf wartet bis die kinder alle fertig sind, da du die Kinder ja vermutlich nicht mehr brauchst, können die sich nach dem Arbeiten von der Bildfläsche verabschieden.
Ist dann das letzte Kind weg, kannste dir die "Final Daten" aus der Pipe holen.

Das die Kinder die Daten in die Pipe legen können, kannst du meines Wissens nur so machen das jeweils 2 Kinder ne Pipe machen, also sollte ein Neues Kind erstellt werden, machste mit dem direkt wieder ne Pipe.

Vater ->PIPE mit kind1 -> Kind1 -> PIPE mit kind 2 -> Kind2 ......

Sollte das letzte Kind sich beenden, schiebt der "Vater des Kindes" die Daten in die Pipe davor und beendet sich erst dann.
So sollte es möglich sein alle berechneten Daten an den UrVater zu übermitteln, bevor die Kinder sich beenden.

Das dieser Prozess unendlich werden kann, kannste mit einer Rekursion machen, ich nehme mal an das du Trennzeichen zwischen den Befehlen hast. Also du splittest den Teil ab den du bearbeiten kannst, den Rest schickste weiter an das nächste Kind bis du ein \0 erreichst, dann ist dein char * leer, und du kannst den Weg wieder zurückgeben.


z.B. "cat /etc/passwd | grep root | tr keine ahnung | awk -s haha"

Dann würde das so aussehen :

Vater speichert sich cat /etc/passwd , erstellt ein Kind weil das \0 nicht erreicht ist und legt die Pipe für Kind 1 "grep root | tr keine ahnung | awk -s haha"

Kind 1 speichert sich "grep root" , erstellt ein Kind weil \0 nicht erreicht ist und legt "tr keine ahnung | awk -s haha" in die Pipe2

Kind 2 speichert " tr keine ahnung" erstellt nen Kind weil \0 nicht erreicht ist und legt "awk -s haha" die Pipe3

Kind 3 speichert "awk -s haha" erkennt nun das ende des char* anhand des \0 und bearbeitet den Befehl, nach dem bearbeiten schickt das Kind das Ergebnis in die PIPE, und beendet sich danach

Kind 2 merkt das sich Kind 3 beendet hat, holt den Teil aus Pipe3 und setzt den bei sich am Befehl an, bearbeitet den Befehl und legt anschließend den Befehl in die Pipe2

Kind 1..... wie bei kind 2

Der Vater erkennt nun das sein Kind tod ist, und nimmt auch den Teil aus der Pipe, bearbeitet ihn und gibt das Endergebnis aus...



Das alles kannst du beliebig lang machen, da irgendwann der Befehl durch das \0 gekennzeichnet ist... und solange Trennzeichen vorhanden sind kannste das immer splitten.
 
hmmmm
ich glaube der vater müsste das letzte kommando ausfuehren.... da mit "cat *" angefangen werden muss.....

die einzelnen befehle stehen in argv[1] - argv[n] ....
habe keine vorstellung wie sich sowas rekursiv lösen lassen könnte.
mein problem ist:
1. prozess fuehrt "cat" aus
und schreibt es in eine pipe
2. prozess liest aus der pipe das bisherige ergebnis
und schreibt es wieder in die pipe (oder eine andere)
.........


Code:
void blub () {
   int pipeeee[2];
   pipe(pipeeee);   

   int iPid = -1;
   iPid = fork();
   if ( iPid == 0) { // KIND
   // fuehre letzten befehl aus
   }
   else {
   wait(&iPid);
   blub();.....
  
   }
 
}

void main(int argc, char **argv) {

if (argc == 2) { // also nur ein kommando
// verarbeitung etc..........
// ........parameter zuschneiden......... exec.. exit(0)....
} /* NUR EIN KOMMANDO*/

else {
blub(); // würde rekursiven aufruf bedeuten
}

keine ahnung wie ich das auf die schnelle rekursiv hinschreibe, da ich mit der rekursion so meine leichten probleme habe.
Vielleicht kannst du mir ein kleines codefragment hier posten.

mfg
 
Code:
char* kinder(char*....)
{
   char* befehl1;
   if(ende vom char* nicht erreicht)
	  neuer prozess
	  {
		 befehl1 = 1. befehl(char*);
		 
		 kinder(rest char* );
	  }
	  else
	  {
		 wait();
	  }
   else //ende erreicht
   {
	   führe befehl aus und gib das ergebnis zurück, entweder per pipe oder per return
	   return char*;
   }
	
}
 
 
 
main(.....)
{
   if ( iPid == 0) { // KIND
	  kinder(char* befehlskette);	 
   }
   else {
   wait(&iPid);
	 
   }
 
	mach mit dem ergenis was du willst ;)
}


So sollte das gehen, ich weiss das es kein Code ist, aber daraus einen zu machen dürfte nicht schwer sein. Habe leider momentan keinen Compiler, da ich nicht zuhause bin, aber ich hoffe das du trotzdem erkennen kannst wie ich das meine ;)

Gruss

MFC OpenGL
 
Hallo Shadow,

die beschriebene Simulation der Bash mit pipes kling sehr interessant. Könntest du vielleicht den fertigen Code bzw. den Teil, wo die Kinder forken und pipes erstellen, posten?

mfg
 
@Shadow, hats geklappt ?

Wenn ja, poste bitte deine Lösung für die anderen, sonst muss das nächste mal das wieder einer erklären ;)

Gruss

MFC OpenGL
 
Zurück