1

I am learning C++ [Java background fwiw] and trying to write a UNIX shell as a project. I am running into a funny little problem with tokenizing the input for execution. The tok function is getting called twice and I'm not sure why. My current test code is the following:

#include <iostream>
#include <vector>
#include <sstream>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>

using namespace std;

void tok(string, char**);

int main(){
    const char* EXIT = "exit";

    string input;

    cout << "shell>> ";
    getline(cin, input);

    pid_t pid = fork();

    char* args[64]; //arbitrary size, 64 possible whitespace-delimited tokens in command
    tok(input, args);
    return 0;
  }

  //copied from http://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
void tok(string inStr, char** args){
    int last = 0, next = 0, i = 0;
    while( (next = inStr.find(' ', last)) != -1){
        cout << i++ << ": " <<  inStr.substr(last, next-last) << endl;
        *args++ = strdup(inStr.substr(last, next-last).c_str());
        last = next + 1;
    }
    cout << i++ << ": " << inStr.substr(last) << endl;
    *args++ = strdup(inStr.substr(last).c_str());
    *args = '\0';
    cout << "done tokenizing..." << endl;
}

My output when I actually run the program is:

$ ./a.out 
shell>> ls -l
0: ls
1: -l
done tokenizing...
0: ls
1: -l
done tokenizing...

I'm not sure why it would do that. Can anyone guide me in the right direction please? Thank you

YazanLpizra
  • 510
  • 10
  • 31
  • 4
    You called `fork` and then called a function. Of course it will be called twice. You need to check the id if you only want one of the processes to call `tok` – AndyG Aug 29 '16 at 18:41
  • While it is up to you, the recommendations I've seen are to not call `using namespace std;` globally. – David C. Rankin Aug 29 '16 at 18:42
  • I see. Thank you. I totally forgot that the fork was called above. If you put your comment as an answer, I'll accept it – YazanLpizra Aug 29 '16 at 18:43
  • The `fork()` function duplicates the process and *both* processes continue to run from the point after calling fork. Why is fork being called? – wallyk Aug 29 '16 at 18:44
  • Also "*I am learning C/C++*" -- well, no. You are either learning C or C++, but they are entirely different (though related) languages at this point in time, and have been for the better part of 3 decades. Good luck with your learning, but are fantastic language. – David C. Rankin Aug 29 '16 at 18:44
  • Side note: lots of `strdup()`s no `free()`s. I would just use `std::vector`. – Johnny Mopp Aug 29 '16 at 18:46
  • @JohnnyMopp Thats correct, I had overlooked that. Ideally, I would want to free after I have executed the commands. I am still trying to figure out how to iterate over the `char**`, I keep getting segmentation faults. – YazanLpizra Aug 29 '16 at 18:52
  • @DavidC.Rankin I see, I will change the question to reflect that. – YazanLpizra Aug 29 '16 at 18:54

3 Answers3

9

The fork function returns twice, once in the original process and once in the newly-created, forked process. Both of those processes then call tok.

There doesn't seem to be any clear reason why you called fork. So the fix may be as simple as eliminating the call to fork.

David Schwartz
  • 166,415
  • 16
  • 184
  • 259
1

following code may work fine

#include <iostream>
#include <vector>
#include <sstream>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>

using namespace std;

void tok(string, char**);

int main(){
const char* EXIT = "exit";

 string input;

 cout << "shell>> ";
 getline(cin, input);

// pid_t pid = fork();

 char* args[64]; 
 tok(input, args);
 return 0;
}


void tok(string inStr, char** args){
int last = 0, next = 0, i = 0;
while( (next = inStr.find(' ', last)) != -1){
    cout << i++ << ": " <<  inStr.substr(last, next-last) << endl;
    *args++ = strdup(inStr.substr(last, next-last).c_str());
    last = next + 1;
}
cout << i++ << ": " << inStr.substr(last) << endl;
*args++ = strdup(inStr.substr(last).c_str());
*args = '\0';
cout << "done tokenizing..." << endl;
}
Abhishek
  • 369
  • 1
  • 8
1

When you call fork, you create two processes. Each process has nearly the exact same state except for the respective pid_t you receive. If that value is greater than 0, then you are in the parent process (main), and otherwise you are in the child (or fork failed).

Without performing a check on the returned pid_t, both processes will call tok, resulting in the double call behavior you witnessed.

Hide the call behind a check on pid like so:

pid_t pid = fork();
if (pid > 0) // have parent process call tok
{
   char* args[64]; //arbitrary size, 64 possible whitespace-delimited tokens in command
   tok(input, args);
}

To see what else parent and child processes have in common (or not): check the docs

AndyG
  • 35,661
  • 8
  • 94
  • 126