开发者

Multi Piping bash-style in C

开发者 https://www.devze.com 2023-02-15 05:29 出处:网络
I know开发者_StackOverflow there are many threads that talk about this problem but I don\'t really understand the way it can be done.

I know开发者_StackOverflow 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:

  • What would be the correct implementation?
  • Is it possible to do it with recursion?
  • Do I need to fork from right to left or left to right (logically it give the same result)?


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消