user2243369
user2243369

Reputation: 63

Piping in custom linux shell

Trying to implement my own linux shell with a few basic functions and of course an issue comes when it's time to implement the pipes. To be more specific the piping works for the most part, though it seems to drop the last argument. For example if I were to run the command

ps ax | grep ps

The ps after the grep command is dropped in my shell. So instead of outputting this as shown using your typical Linux shell

339 ?        S      0:00 upstart-udev-bridge --daemon
497 ?        Ss     0:00 /usr/sbin/cupsd -F
503 ?        S<     0:00 [kpsmoused]
720 ?        S      0:00 upstart-socket-bridge --daemon
5541 pts/0    R+     0:00 ps ax
5542 pts/0    S+     0:00 grep --colour=auto ps

You get this

339 ?        S      0:00 upstart-udev-bridge --daemon
497 ?        Ss     0:00 /usr/sbin/cupsd -F
503 ?        S<     0:00 [kpsmoused]
720 ?        S      0:00 upstart-socket-bridge --daemon
5557 pts/0    R+     0:00 ps ax

In this case you're not searching for the matching pattern ps.

The function for piping runs as follows

void mypipes(char* args[], int nargs)
{
  pid_t pid;
  int fd[2];

  char* cmdargs[nargs - 2];
  char* cmdargs2[nargs - 2];


  int i;
  int t = 0;
  int count = 0;

  for(i = 0; i < nargs; i++)
  {
if(!strcmp(args[i], "|"))
{
    //dont put into array
    t = 1;
}
else if(t == 0)
{
    cmdargs[i] = args[i];
    count++;
}
else if(t == 1)
{
    cmdargs2[i - 3] = args[i];
}
  }


  if(count == 2)
  {
  pipe(fd);
  pid = fork();

  if(pid == -1)
  { 
    perror("unable to fork");
    exit(1);
  }
  if(pid > 0)
  {
    wait(&pid);
    close(fd[1]);
    close(0);
    dup2(fd[0],0);
    execlp(cmdargs2[0], cmdargs2[0], cmdargs2[1], NULL);
  }
  if(pid == 0)
  {
    close(fd[0]);
    close(1);
    dup2(fd[1],1);
    execlp(cmdargs[0], cmdargs[0], cmdargs[1], NULL);
  }
  }

  if(count == 1)
  {
  pipe(fd);
  pid = fork();

  if(pid == -1)
  { 
    perror("unable to fork");
    exit(1);
  }
  if(pid > 0)
  {
    wait(&pid);
    close(fd[1]);
    close(0);
    dup2(fd[0],0);
    execlp(cmdargs2[0], cmdargs2[1], NULL);
  }
  if(pid == 0)
  {
    close(fd[0]);
    close(1);
    dup2(fd[1],1);
    execlp(cmdargs[0], cmdargs[1], NULL);
  }

  }

}

I've checked to see if all the variables after the pipe are still within the second set of arguments and they are the issue isn't the main but somewhere where I'm executing the actual piping where it doesn't read to the end.

Thank you in advance for any tips on what could be wrong here.

Upvotes: 2

Views: 1617

Answers (1)

TripeHound
TripeHound

Reputation: 2970

First, unless I'm missing something, your two sample ouputs both look like they're working: they both only list lines with 'ps' in them. (If it really was running ps ax | grep then grep would complain about usage). The only difference I can see in the outputs is that the second doesn't list the grep process itself, but this can easily happen if the ps has finished grabbing the process-list before the grep is started.

Second, your use of wait(&pid) is odd -- as it's in the child process, it will wait for any of the child-process's children to exit. As there are none, it will return ECHILD (and if there were, it would overwrite pid with the grandchild's exit status).

Third, your use of t, cmdargs and cmdargs2 together with count to decide which execlp call to make only works if it's of the form cmd1 arg1 | cmd2 arg2 -- almost any other combinaation won't work. A couple of points:

  • instead of [i -3] to save the arguments after the pipe, you need to remember at which position you saw the pipe (e.g. instead of t=1, use t=i+1 when you find the pipe and cmdargs2[i-t] when saving the arguments after it).
  • you'll want to look at the execvp() version of the function call, so that you can pass in arrays of arguments (remembering to add a NULL element after all the ones from the command-line).

Upvotes: 1

Related Questions