Home > Mobile >  How to execute multiple commands from argv with an specific delimiter
How to execute multiple commands from argv with an specific delimiter

Time:11-24

Recently I had an assignment where I had to make a program that takes from command line two different commands, separated with a ' ', for example:

ps -lu myUsername   ls -la

The objective of the program was to run any two orders simultaneously, with any number of parameters per order, by using fork() and exec(). This was my solution to this problem:

Note: the original problem was meant to be run on a machine with Solaris 5.10 and c89 or c99 standard (gcc 3.4.3)

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <strings.h>

int main (int argc, char* argv[]) {
    char delimiter = ' ';
    char* auxp; //auxiliar pointer
    int i = 1;
    int position;
    
    while(i < argc){
        if (strcmp(" ", argv[i]) == 0) {
            argv[i] = NULL;
            position = i;
        }
        i  ;
    }

    if (fork() == 0) {
        execvp(argv[1], &argv[1]);
        exit(1);
    }

    if (fork() == 0) {
        execvp(argv[position 1], &argv[position 1]);
        exit(1);
    }

    wait(NULL);
    wait(NULL);
    exit(0);
}

This was enough for the assignment but I wanted to make it work with N arguments instead of only 2. I can't reach a systematic way to find the all the addresses. Any help is appreciated, thanks in advice.

CodePudding user response:

Just move the forking into the loop:

int main (int argc, char* argv[])
{
    char** start =   argv;
    unsigned int n = 0;
    for(; *argv;   argv) // profiting from argv being null terminated, too...
    {
        if (strcmp(" ", *argv) == 0)
        {
            *argv = NULL;
            if (fork() == 0)
            {
                execvp(*start, start);
                exit(1);
            }
            start = argv   1;
              n; // but need to count how many times we actually forked!
        }
    }

    while(n--)
    {
        wait(NULL);
    }
    exit(0);
}

OK, I modified iterating a bit, too – pointers are just so much nicer (personal oppinion...).

Note: This is untested code, if you find a bug please fix yourself...

CodePudding user response:

For the general case of N commands, need to keep track of where each command starts and ends, and fork a new child whenever the end of a command is found. That could be at a separator argument, or at the end of the original argument list.

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char* argv[]) {
    /*
     * Note: argc could be in range 0 to INT_MAX.
     * Using unsigned int values to avoid arithmetic overflow.
     */
    unsigned int start = 1;
    unsigned int end = 1;
    unsigned int child_count = 0;

    /* Note: argv[argc] is a terminating null pointer. */
    while(end <= (unsigned int)argc){
        if(end == (unsigned int)argc || strcmp(" ", argv[end]) == 0){
            /* Reached the terminating null pointer or a command separator. */
            argv[end] = NULL;
            if(start != end){
                /*
                 * Command is not empty.
                 * Fork a child process to execute the command.
                 */
                pid_t child = fork();
                if(child > 0){
                    /* Parent forked child successfully. */
                    child_count  ;
                }else if(child == 0){
                    /* This is the child process. Execute command. */
                    execvp(argv[start], &argv[start]);
                    exit(1);
                }
            }
            /* Next command starts after this one. */
            start = end   1;
        }
        /* Looking for terminating null pointer or command separator. */
        end  ;
    }

    /* Wait for the child processes to terminate. */
    while(child_count){
        wait(NULL);
        child_count--;
    }
    exit(0);
}

Note: the argv[end] = NULL; line could be moved into the if(child == 0){ } block (but before the call to execvp) to leave the parent's original argument list intact.

  • Related