简体   繁体   中英

Use execve with argv (Piping)

I got the piping of several commands working. Now Id like to execute this with received arguments ( *argv[] ) and not hard coded like here.

Ived tried using the rest of the execve family but no progress. Ive been thinking to put everything (argv) in an array of pointers and adding NULL at the end of where each pointer points, then iterate through it.

Passed argument ( , included): echo "test" , wc , cowsay

int main(int argc, char *argv[]) {

    char *echo[] = {"echo", "test", NULL};
    char *rev[] = {"rev", NULL};
    char *wc[] = {"wc", NULL};
    char **cmd[] = {echo, wc, rev, NULL};

    loop_pipe(cmd);
    return (0);
}

void loop_pipe(char ***cmd) {
    int p[2];
    pid_t pid;
    int fd_in = 0;

    while (*cmd != NULL) {
        pipe(p);
        if ((pid = fork()) == -1) {
            exit(EXIT_FAILURE);
        } else if (pid == 0) {
            dup2(fd_in, 0); //change the input according to the old one
            if (*(cmd + 1) != NULL)
                dup2(p[1], 1);
            close(p[0]);
            execvp((*cmd)[0], *cmd);
            exit(EXIT_FAILURE);
        } else {
            wait(NULL);
            close(p[1]);
            fd_in = p[0]; //save the input for the next command
            cmd++;
        }
    }
}

You did all the multiprocessing part. You only need to do some array shuffling, to convert array of strings to array of arrays of strings.

If you don't mind iterating the array two times, you can do like the following:

int main(int argc, char *argv[]) {
    // count the commas
    int n = 0;
    for (int k = 1; k < argc; k++) {
        if (strcmp(argv[k], ",") == 0)
            n++;
    }
    int stage_count = n + 1;

    // allocate the pipeline stage list
    char ***cmd = calloc(stage_count + 1, sizeof(char**));

    // fill arguments for each stage
    int stage = 0;
    int first_arg = 1; // index of the first argument of current stage
    for (int k = 1; k <= argc; k++) {
        // stage arguments are terminated by a comma,
        // except of the last, where k == argc
        if ((k == argc) || (strcmp(argv[k], ",") == 0)) {
            // now, k is one greater than the index of the last argument of that stage
            int arg_count = k - first_arg;

            // save arguments for one pipeline stage
            // (note that only char pointers are copied, not the data they point to)
            // (also, you can use `malloc` but don’t forget to write terminating NULL then)
            char **args = calloc(arg_count + 1, sizeof(char*));
            memcpy(args, argv + first_arg, arg_count * sizeof(char*));
            cmd[stage] = args;

            stage++;
            first_arg = k + 1; // k refers to the delimiter so the next stage arguments start at k+1
        }
    }

    loop_pipe(cmd);
    return 0;
}

You can refine this using special properties of argv , eg that argv[argc] == NULL , or that argv , as well as the argument strings themselves, is actually writable .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM