Problem bei übergabe von char *array[] / execvp aufruf
-
Hallo liebe Community
,
Ich möchte eine (UNIX) Shell schreiben, welche einen neuen Prozess erzeugt und und mittels execvp(command,parameters) ausführt.
Für mein eigentliches Problem habe ich den Code(unten) gekürzt.Für die Übergabe an execvp habe ich,wie unten zu sehen,ein char Array command und ein Zeiger auf ein char Array parameters angelegt.
In der Funktion read_command(...) habe ich eine Testausgabe geschrieben, welche wie erwartet ausgegeben werden.
Nach aufruf von read_command(...) bekomme ich command angezeigt, die parameter, welche oben Funktioniert haben leider nicht.Sicher gibt es insgesammt auch eine bessere möglichkeit die Zeile(wholeLine) zu zerlegen und in command bzw parameters[] zu speichern, welche ich leider nicht gefunden habe

#include <cstdlib> #include <iostream> #include <string> #include <sstream> #define MAXLINE 100 void read_command(char *command, char *parameters[]) { // prompt for user input and read a command line std::string wholeLine; std::string comm; std::string params[30]; // 30 just for Test std::string tmpString; std::getline(std::cin, wholeLine); char tmpCString[MAXLINE]; std::stringstream ss(wholeLine); ss>>command; // First Word in command int i=0; while(ss.good()) { ss>>tmpString; //ss>>parameters[i]; strncpy(tmpCString,tmpString.c_str(), sizeof(tmpCString)); tmpCString[sizeof(tmpCString)-1]=0; // Nullterminieren sicherheitshalber params[i++]=tmpCString; } for (int x =0; x<i; x++) { std::string tmpS; tmpS=params[x]; strncpy(tmpCString, tmpS.c_str(), sizeof(tmpCString)); tmpCString[sizeof(tmpCString)-1]=0; // Nulterminieren parameters[x]=tmpCString; std::cout <<"test in read_command "<<x<<" "<<parameters[x]<<std::endl; // Testausgabe Funktioniert an dieser Stelle noch } } // read_command int main(int argc, char *argv[]) { char command[MAXLINE]; char *parameters[MAXLINE]; read_command(command, parameters); // read user input std::cout<<"Command: "<<command<<std::endl; // Ausgabe von command funktioniert wie erwartet std::cout<<"param1 "<<parameters[0]<<std::endl; // Ausgabe von parameters[0]und parameters[1] funktioniert leider nicht. std::cout<<"param2 "<<parameters[1]<<std::endl; return (0); }Nach Start der Programms und eingabe von "befehl foo bar" erscheint folgende ausgabe in der Konsole:
"befehl foo bar
test in read_command 0 foo
test in read_command 1 bar
Command: befehl
param1
param2 "Nach param1 sollte eigentlich "foo" und nach param2 "bar" stehen, wie es oben auch funktionierte.
Ich hoffe ihr versteht mein Problem und könnt mir Hilfe diesbezüglich geben.
Danke schonmal für eure Mühe

-
Da du sowieso schon teilweise die C++ Datentypen verwendest, schreib deine Funktion um zu:
void read_command(string &command, vector<string> ¶meters)PS: Das C-Äquivalent wäre übrigens:
char parameters[MAXPARAMETERS][MAXLINE]
-
Hallo und danke für deine schnelle Antwort

Klar! Die Variable tmpCString ist nur in der Funktion gültig, das habe ich wohl nicht beachtet.Wie du es vorgeschlagen hast(mit einem Vector), hätte ich meine Funktion eigentlich auch geschrieben, nur ist mir der aufruf von execvp(...) anschließend nicht möglich oder ?
Verstehe ich das richtig:
char *parameters[MAXLINE] ist eine misxhung von c++ und c und sollte so nicht genutzt werden ?
-
DaJon_ schrieb:
Wie du es vorgeschlagen hast(mit einem Vector), hätte ich meine Funktion eigentlich auch geschrieben, nur ist mir der aufruf von execvp(...) anschließend nicht möglich oder ?
Wieso nicht? Aus einen std::string kann man doch leicht einen C-String machen.
Verstehe ich das richtig:
char *parameters[MAXLINE] ist eine misxhung von c++ und c und sollte so nicht genutzt werden ?Nein, diese Zeile selbst ist einfach typisches C. In C++ würde man das nie so machen. Der Rest deines Programms ist aber (teilweise) C++. Daher ist dein Gesamtprogramm eine Mischung aus C und C++. C und C++ arbeiten aber nicht sonderlich gut zusammen.
-
Wieso nicht? Aus einen std::string kann man doch leicht einen C-String machen.
Ja, nur muss ich aus einem Vector ein 2 Dim. C Array machen, wenn ich das richtig verstehe?
-
DaJon_ schrieb:
Wieso nicht? Aus einen std::string kann man doch leicht einen C-String machen.
Ja, nur muss ich aus einem Vector ein 2 Dim. C Array machen, wenn ich das richtig verstehe?
Welche Version von execvp willst du denn einsetzen?
-
Ehrlich gesagt kenne ich nur eine Version:
int execvp(const char *file, char *const argv[]);
-
Dann würde ich vor dem Aufruf einen
vector<const char*>erstellen, diesen mit denc_str()s aus demvector<string>füllen und dann einen Zeiger auf das erste Element (alsodata()) an execvp übergeben.
-
Das klingt gut vielen dank

Nur weiss ich nicht, wie ich einen Zeiger auf das erste Element übergebe und was du mit data() meinst ?
-
DaJon_ schrieb:
Das klingt gut vielen dank

Nur weiss ich nicht, wie ich einen Zeiger auf das erste Element übergebe und was du mit data() meinst ?Na, entweder
&mein_vector[0]oder ebenmein_vector.data().
-
Ah danke

Leider funktioniert mein execvp Aufruf noch nicht:"No matching function for to call 'execvp'"
int status; std::vector<const char *> params; const char* comm = command.c_str(); // Füllen des Vectors, fork() etc. status = execvp(comm,params.data());Habe mir execvp nochmal angeschaut:
int execvp(const char *, char * const *);warum kann ich die Funktion nicht aufrufen ?

-
Hier mal ein Beispiel: http://stackoverflow.com/questions/5846934/how-to-pass-a-vector-to-execvp
Eventuell musst du deinem Compiler mitteilen das er C++11 oder neuer verarbeiten soll.
Ältere C++11 Compiler erfordern bei Strings in einigen Fällen die C-Variante. Mit C++11 werden Strings mehr im C++ Stil unterstützt.
MfG f.-th.
-
Was genau hat das mit älteren oder neueren C++11-Compilern zu tun?
@OP:
Das Problem ist, dass execvp ein char * const * (Zeiger auf konstante Zeiger auf Char) erwartet, Du ihm aber ein char const * (const) * (Zeiger auf Zeiger auf konstante Char) gibst. Der Vector muss ein vector<char*> sein (das zunächst mal unabhängig von der Sprachversion).
Da aber string::c_str einen Zeiger auf const liefert, musst Du hier hässlicherweise das const wegcasten, sonst geht es nicht. Das ist an dieser Stelle aber sauber, da wir "wissen", dass execvp die Strings nicht verändert.
Hint: &command[0], um einen nicht-konstanten Zeiger auf den String zu bekommen, ist nicht ratsam, da der String dann nicht zwingenderweise nullterminiert sein muss.
-
LordJaxom schrieb:
Da aber string::c_str einen Zeiger auf const liefert, musst Du hier hässlicherweise das const wegcasten, sonst geht es nicht. Das ist an dieser Stelle aber sauber, da wir "wissen", dass execvp die Strings nicht verändert.
So hat es geklappt. vielen lieben Dank

-
Nachdem der Aufruf funktioniert hat, habe ich leider ein weiteres Problem.
Der Vector wird scheinbar nicht richtig übergeben..// ... int status; std::string command; std::vector<const char *> params; // command und params wird gefüllt... // Testausgabe for (int i=0; i<params.size(); i++) { std::cout<<"params in Kind an stelle "<<i<" "<<<params[i]<<std::endl; } std::cout<<"Command in Kind : "<<command<<std::endl status= execvp(const_cast<char*>(command.c_str()),const_cast<char**>(params.data())); // hier das Problem ?Nach eingabe von "echo test" erscheint folgende Ausgabe:
"params in Kind an stelle 0 test
Command in Kind : echo
// leerzeile
"Der echo Befehl wird also ausgeführt, sonst wäre keine leerzeile vorhanden, allerdings verschluckt er das erste Argument.
Wenn ich allerdings "echo test1 test2" eingebe erscheint folgende Ausgabe:
"params in Kind an stelle 0test1
params in Kind an stelle 1test2
Command in Kind : echo
test2"Hier wird also der echo Befehl ausgeführt, allerdings nur mit dem 2.Parameter und der Erste wird wieder verschlungen.
Jemand eine Idee woran das liegen könnte ?
LG
-
man execvp schrieb:
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer.
-
Soo bin wieder ein Stück weiter doch hänge erneut an einem Problem:
//... std::vector<char*> params; while(1){ // Füllen von params etc. // params.push_back(NULL); // Bei Ausführen dieses Befehls erscheint gar keine Ausgabe // Testausgabe for (int i=0; i<params.size(); i++) { std::cout<<"params in Kind an stelle "<<i<" "<<<params[i]<<std::endl; } status= execvp(const_cast<char*>(params[0]),const_cast<char**>(¶ms.front())); //... params.clear(); }// ende while-SchleifeBei eingabe von "echo test1 test2":
"
params in Kind an stelle 0 echo
params in Kind an stelle 1 test1
params in Kind an stelle 2 test2
test1 test2"-Also genauso wie ich es erwartet habe.
Gebe ich anschließend "echo test3" ein erscheint folgende Ausgabe:
"
params in Kind an stelle 0 echo
params in Kind an stelle 1 test3
test3 test2"-Der vector params hat wie erwartet nur 2 Stellen. Trotzdem wird mir der 2. Parameter vom Durchlauf davor mit ausgegeben.
Meine Vermutung:Der compiler(?) weiß also nicht, wo er aufhören soll die Argumente auszulesen, die trotz des params.clear() befehl noch im Speicher vorhanden sind ?Das anfügen von NULL (wie oben auskommentiert) führt allerdings dazu, dass gar keine Ausgabe erscheint, was mich verwundert

Ich hoffe ihr könnt mir weiterhelfen

LG
-
Leider sieht man an Deinem Code nicht, wann Du NULL in den Vector pusht. Jedenfalls sieht man in der Ausgabe nichts davon, dass am Ende des Vectors noch ein Element mit dem Inhalt NULL vorhanden ist.
Tipp für die Korrektur: NULL nach cout zu schreiben ist undefined, Du müsstest also dort etwas wie
std::cout << (params[i] != NULL ? params[i] : "NULL")"machen.
Und benutze nullptr
