r3dalice
r3dalice

Reputation: 119

Multi Piping bash-style in C

I know there are many threads that talk about this problem but I don't really understand the way it can be done.

I'm trying to make a shell that can execute a linux command sucha as ps | grep | less I've donne the parsing by puting every command and its args in a simply linked list.

Here's my implementation that doesn't work. Hope that's clear enough.

if ((son = fork()) < 0)
  return printerr_sys("Unable to fork", 0);
if (son == 0)
  {
    if (first > 1 && data->format[first - 1] &&
    is_directing_elt(data->format[first - 1]) == DIRECT_TPIPE)
       dup2(tube_p[0], STDIN_FILENO);
       first = make_argv(data, first, &argv);
    if (next)
     {
      dup2(tube_v[1], STDOUT_FILENO);
      close(tube_v[0]);
     }
    if (execvp(argv[0], argv) < 0)
      return printerr_cmd(argv[0], 1);
  }
else
  {
    if (next)
        {
           close(tube_v[1]);
           cmdline_executer(data, next, tube_v);
        }
    waitpid(son, &(data->lastcmd), WUNTRACED);
    data->lastcmd = WEXITSTATUS(data->lastcmd);
  }
return TRUE;

My questions are:

Upvotes: 1

Views: 3017

Answers (2)

Oscar Mederos
Oscar Mederos

Reputation: 29843

Here's a part of a UNIX Shell I had to implement in C for Operating System subject in my Computer Science career.

/* Executes the command 'buffer' assuming that doesn't contain redirections */
void execute_only_pipes(char* buffer)
{
    char *temp = NULL, *pipeCommands[MAX_PIPES], *cmdArgs[MAX_ARGUMENTS];
    int newPipe[2], oldPipe[2], pipesCount, aCount, i, status;
    pid_t pid;

    pipesCount = -1; /* This variable will contain how many pipes the command contains */

    /* Counting the number of pipes and splitting them into pipeCommands */
    do 
    {
        temp = strsep(&buffer, "|");

        if(temp != NULL) 
        {
            if(strlen(temp) > 0) 
            {
                pipeCommands[++pipesCount] = temp;
            }
        }
    } while(temp);

    cmdArgs[++pipesCount] = NULL;

    for(i = 0; i < pipesCount; i++) /* For each command */
    {
        aCount = -1;

        /* Parsing command & arguments */
        do 
        {
            temp = strsep(&pipeCommands[i], " ");
            if(temp != NULL) 
            {
                if(strlen(temp) > 0) 
                {
                    /* If a parameter is ~, then replace it by /home/user */
                    if (!strcmp(temp, "~"))
                        strcpy(temp, home);
                    cmdArgs[++aCount] = temp;
                }
            }
        } while(temp);

        cmdArgs[++aCount] = NULL;

        /* If there still are commands to be executed */
        if(i < pipesCount-1) 
        {
            pipe(newPipe); /* just create a pipe */
        }

        pid = fork();

        if(pid == 0)  /* Child */
        {
            /* If there is a previous command */
            if(i > 0)
            {
                close(oldPipe[1]);
                dup2(oldPipe[0], 0);
                close(oldPipe[0]);
            }

            /* If there still are commands to be executed */
            if(i < pipesCount-1) 
            {
                close(newPipe[0]);
                dup2(newPipe[1], 1);
                close(newPipe[1]);
            }

            /* Execute it */
            int res = execvp(cmdArgs[0], cmdArgs);

            if (res == -1)
            {
                printf("Error. Command not found: %s\n", cmdArgs[0]);
            }
            exit(1);
        } 
        else /* Father */
        {
            /* If there is a previous command */
            if(i > 0) 
            {
                close(oldPipe[0]);
                close(oldPipe[1]);
            }

            /* do we have a next command? */
            if(i < pipesCount-1) 
            {
                oldPipe[0] = newPipe[0];
                oldPipe[1] = newPipe[1];
            }

            /* wait for last command process? */
            if(i == pipesCount-1) 
            {
                waitpid(pid, &status, 0);
            }
        }
    }
}

It might be a little buggy (I'm not checking if fork() < 0, etc) but the main idea is correct.

> Is it possible to do it with recursion?

Most of the time I try to avoid recursion, if I can write a similar understandable code without using it.

Upvotes: 5

Jim Balter
Jim Balter

Reputation: 16406

Processes run independently, so you need to set up the pipe for at least the first pair of commands before you fork, but you're doing that in the child (son == 0). You could code a recursive solution that, as long as there are at least two commands left, creates a pipe, then forks, then runs the first command.

Upvotes: 0

Related Questions