For the past few days I have been attempting to write my own shell implementation but I seem to have gotten stuck on getting pipes to work properly. I am able to parse a line and fork off the commands between the pipes (ex: ls | sort) individually but can't seem to get them to pipe input from one into the other.
I think I just don't understand how to use dup2() and pipes properly.
I've now included my code which is still failing... :( So stuck...
void forkAndExecute( char* arrayOfWords[] , vector<pid_t> *vectorOfPIDs , bool hasNextCmd , bool hasPrevCmd) {
int fd[ 2 ];
pid_t pid;
if( hasNextCmd ){
pipe(fd);
}
pid = fork();
//error if PID < 0
if( pid < 0 ) {
cerr << ">>> fork failed >>>" << endl;
exit(-1);
}
//child process if PID == 0
else if( pid == 0 ) {
if ( hasPrevCmd ){
dup2(fd[0] , 0);
close(fd[0]);
close(fd[1]);
}
if ( hasNextCmd ){
dup2(fd[1],1);
close(fd[0]);
close(fd[1]);
}
execvp( arrayOfWords[0] , arrayOfWords );
cout << ">>> command not found >>>" << endl;
//if logic reaches here, exec failed
exit(0);
}
//parent process
else{
close(fd[0]);
close(fd[1]);
//if( ! isLastCmd ){
//}
vectorOfPIDs->push_back(pid);
}
}
Here's a tutorial on UNIX pipes, specifically about how to construct piplines in a shell-like architecture:
http://www.cse.ohio-state.edu/~mamrak/CIS762/pipes_lab_notes.html
Not much fully-written code, but it describes the concepts pretty well.
You could also download source code for virtually any shell, such as bash, tcsh, zsh, etc.
First suggestion: Symbolic constants are better than magic numbers.
Second suggestion: Take a step back and think about what you're trying to accomplish.
You want to spawn two processes, with the first process's stdout connected to the second process's stdin. Right?
So, in C, this means that you need to take call
pipe
, passfd[PIPE_WRITE]
to the first child process, which willdup2
it to 1, and passfd[PIPE_READ]
to the second child process, which willdup2
it to 0.Simply looking at
forkAndExecute'
s prototype shows that it can't do that:It only handles a single command, and from looking at that argument list, unless it resorts to evil global variables, there's no way for it to receive a file descriptor from its PrevCmd or receive a file descriptor from its NextCmd.
Think about how to manage the file descriptors that you need, and redesign
forkAndExecute
to be able to use these.ok This is working for me. Hope this helps you:
When I needed to do a similar shell some years ago, I used the book Practical Unix Programming.
It is really useful for examples on many IPC topics. I still have a copy on my desk that I reference from time to time. For $2 - $9 used, it's a pretty good value for what you get.
For what it's worth, just thought I'd mention it.
Try reading the source code of Bash to see how they did it.
The general process would add error handling to this base process (pseudocode):
Create the pipe first. Then fork the child processes so they inherit it. Remap the file descriptors to 0 (stdin) and 1 (stdout) so the processes read and write the appropriate places. Close any remaining file descriptor you don't want the child processes to see or block on when the work is finished. Exec the actual child processes. Wait for them to finish, and you're done!