0

I'm fine executing commands like "ls" and stuff like that but I want to do something like "ls | sort" but the execvp system call doesn't support "|". How can I do this using only system calls? when I try something like

char *arg[] = {"ls","|","sort",NULL};
execvp(arg[0],arg);

it doesn't work, how can I do this?

Edit:

char* execString (char string[]){
int link[2];
pipe(link);

if (fork() == 0){
    int i = 0;
    char *p = strtok(string," ");
    char *x[spacecount(string)+2];

    while(p){
        x[i++] = p;
        p = strtok(NULL," ");
    }
    x[i] = NULL;

    dup2(link[1],1);
    close(link[0]);
    close(link[0]);

    execvp(x[0],x);
    _exit(0);
} else {
    wait(NULL);
    close(link[1]);
    char buf[512];
    int i = 0;

    while (read(link[0],&buf[i++],1) == 1);

    close(link[0]);
    buf[i-2] = '\0';

    return strdup(buf); 
    }
}

This is the function i'm executing to exec a string that contains a command, its return value is a pointer to a string that contains the output from that command, how can I use that output as the input to a new command using execvp or another function from the exec family?

Edit2: So I made a new function that receives two strings as argument and execs the first one then the second one using as input the output from the first exec, I thought it was working fine it worked with ls | head -1 and other variations of ls but when I do something like ls | sort -R it doesn't work, i've tried several things and I can't understand why this is happening, here is the code:

char* execStrings (char previousstring[], char string[]){
int link[2];
pipe(link);

if (fork() == 0){
    int i = 0;
    char *previouscommand[spacecount(previousstring)+2];
    char *temp = strtok(previousstring," ");
    while(temp){
        previouscommand[i++] = temp;
        temp = strtok(NULL," ");
    }
    previouscommand[i] = NULL;

    dup2(link[1],1); /*  stdout result redrecting to write end of pipe */
    close(link[1]);
    close(link[0]);
    execvp(previouscommand[0],previouscommand);

} else {
    wait(NULL);
    int res[2];
    pipe(res);

    if(fork() == 0){
        int i = 0;
        char *temp = strtok(string," ");
        char *command[spacecount(string)+2];

        while(temp){
            command[i++] = temp;
            temp = strtok(NULL," "); 
        }
        command[i] = NULL;

        dup2(link[0],0);
        close(link[0]);
        close(link[1]);

        dup2(res[1],1);
        close(res[1]);
        close(res[0]);

        execvp(command[0],command)
    } else {
        wait(NULL);

        close(res[1]);
        char buf[512];
        int i = 0;

        while (read(res[0],&buf[i++],1) == 1);

        close(res[0]);
        buf[i-2] = '\0';

        return strdup(buf);
    }
}
}
9
  • 1
    You have to fork and exec. cs.uleth.ca/~holzmann/C/system/pipeforkexec.html Commented May 31, 2018 at 16:43
  • or a good old violent system("ls | sort") Commented May 31, 2018 at 16:44
  • Pipes like that are handled by the shell. But you could easily implement it yourself. There are many tutorials and examples using pipes, fork and exec if you look around a little. Commented May 31, 2018 at 16:44
  • @Jean-FrançoisFabre OP said "using only system calls". Commented May 31, 2018 at 16:44
  • I understood system calls Commented May 31, 2018 at 16:45

2 Answers 2

2

you want to do something like ls | sort but the way you are doing like

char *arg[] = {"ls","|","sort",NULL};
execvp(arg[0],arg); /*it won't work */

won't work because here you are calling execvp on ls and sort which are two separate process not single process. Also

ls     |    sort  => output of process-1 make as input to process-2 & execute it   
|            |
process-1  process-2

To achieve the above create two process by calling fork() and use exec() family function to replace ls and sort in child & parent process.

here is the sample code

int main(void) {
        int p[2];
        pipe(p);
        char *arg[] = {"ls","sort",NULL};
        if(fork()==0) {
                close(0);/* close the stdin stream so that this 
process shoulbn't read from stdin */
                dup(p[0]);/* read from read end of pipe */
                close(p[1]);
                execlp(arg[1],arg[1],(char*)NULL);
        }
        else{
                close(1);/* close the stdout stream, so that o/p shouldn't print on monitor */
                dup(p[1]); /*  stdout result redrecting to write end of pipe */
                close(p[0]);
                execlp(arg[0],arg[0],(char*)NULL);
        }
        return 0;
}
Sign up to request clarification or add additional context in comments.

4 Comments

execlp(arg[0],arg[0],NULL); is potentially broken if your library defines NULL as 0. execlp is a varargs function and that code would pass 0 as an int. Fix: execlp(arg[0], arg[0], (char *)NULL);
@achal thanks for the answer but, in the last line of the child process, how can I exec the sort command with the output from ls? I can read the output from the pipe but I can't figure out how to use it as the input to the next command
@rybur The code already does that. What is there to "figure out"?
yeah sorry, it's just that ls | sort produces the same output as ls so I wasn't figuring that out, thanks everyone
1

| is a shell feature. You need to do the same thing the shell does, i.e. use pipe, fork, dup2, execvp to create a pipe, spawn a new process, and connect the pipe to the processes' stdin and stdout, respectively.

Take the code from your execString function but replace the else block. In the parent process you should dup2(link[0], 0) (connect the read end of your pipe to stdin), then execvp the other program (e.g. sort).

Remember to close the pipe ends you don't need! I.e. in the producer process, close(link[0]); in the consumer process, close(link[1]). If you forget this part, things may get "stuck" (commands seemingly hanging forever).

Of course, if you want your original program to keep going, you need to wrap that code inside another fork (and waitpid for it).


As an aside, your execString is broken if the command outputs a lot of data. At some point the pipe will get full and the command will pause, waiting for another process to drain the pipe (by reading from it). Your program will be stuck in wait, waiting for the command to terminate. The result is deadlock.

To fix this issue, only call wait after you're done reading from the pipe.

3 Comments

at the moment I can execute a simple command, like "ls -l -i" with a fork and dup2 the stdout to a pipe I created to save the output but I don't understand how I can then use that output as the input to a new command like sort, if I just put the output as a new string in the execvp it won't work
@rybur What do you mean by "a pipe I created to save the output"? Can you show your code?
I eddited the post to include the code of my exec function

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.