Simple Shell in C++

I am new to linux programming, and to get my bearings, I have been writing some small programs. I have already written a ls clone that will do ls, ls -l, and ls -lR. My current project is to write a simple shell. My understanding on how to do this is to loop the following: Get some input from the user and parse it into an array of cstrings, fork the program, have the child exec the user's command. I tried this, but I found that I lost i/o with the child after the exec, which make sense considering what it does. I did some reading online, I found that I need to use pipes to connect the child stdout and stdin to the parents before the exec. I did a few experiments with the pipes, but they all either crashed or didn't work, so I'm clearly missing something.

Is my understanding of what I need to do correct? Am I missing some small, but important detail? Is it even possible to connect out pipe to the parent's stdout so the child's output is automatically output to the console, or do I need to manually read the pipe and output its contents. Can you give me some examples of using pipes in this context, preferably in C++? Thanks in advance.


My code so far:
[code]
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

const unsigned int MAX_ARGS = 128;

using namespace std;

// Execute a parsed command line returning the command's exit code
int doit(const vector& tok)
{
if (!tok.size() || tok[0] == "") return 0;
// If user entered cd, change to specified dir. If no dir specified, change to the users home dir
else if (tok[0] == "cd")
{
if (tok.size() > 1) chdir(tok[1].c_str());
else chdir(getenv("HOME"));
return 0;
}

//Else execute the command specified by the user
if (pid_t kidpid = fork())
{
//Parent
int status = 0;
if (tok.back().at(tok.back().size() - 1) != '&')
{
waitpid(kidpid, &status, 0);

#ifdef _INSPECT_EXIT_STATUS
cout << "exit status=" << status << endl;
#endif //_INSPECT_EXIT_STATUS
}
return status;
}
//Child - execute the command
char* arglist[MAX_ARGS];
for (unsigned int x = 0; x < tok.size() && x < MAX_ARGS - 1; x++)
strcpy(arglist[x], tok[x].c_str());
arglist[tok.size()] = NULL;

execvp(tok[0].c_str(), arglist);

//Program will never reach here unless execvp failed
cerr << "execpv failed: " << strerror(errno) << endl;
exit(errno);
}

int main(int argc, char* argv[], char* envp[])
{
while (!cin.eof())
{
cout << "? ";
string temp;
getline(cin, temp);
if (temp == "exit") break;

vector<string> v;
//Break string into separate strings on whitespace
{
stringstream foo(temp);
string s;
while (foo >> s)
{
if (s[0]=='~') s = getenv("HOME") + s.substr(1);
v.push_back(s);
}
}
doit(v);
}
cout << "exit" << endl;
return 0;
}[/code]

Comments

  • I found that my problem was that I was secretly seg faulting in the child, which was causing the child to silently fail. The code works if you substitute the strcpy() with "arglist[x] = strdup(tok[x].c_str());" which actually allocates memory. The pipes are only needed for commands like "ps aux | grep bragr" in which the output of one child needs to be piped to the input of another.
  • Hi,
    I was wondering if you ever got your '|' '>' '<' up and running on your shell?

    thnks
  • I did but if you go to ucr, I'm not going to give you code. :)
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories