6

I am trying to code a shell. But my shell doesn't execute the command - ls -l | less. I am using execvp. the code is given below.

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(){
    int pid, status, num, len;
    char str[1000], cwd[100];
    char* word[100];

    getcwd(cwd, sizeof(cwd));

    while(1){
        chdir(cwd);

        printf("%s > ", cwd);

        gets(str);

        pid=vfork();

        if(pid == 0){
            num = 0;
            word[num] = strtok (str, " ");

            while (word[num] != NULL) {
                word[num] = strdup (word[num]);
                len = strlen (word[num]);
                if (strlen (word[num]) > 0)
                    if (word[num][len-1] == '\n')
                        word[num][len-1] = '\0';
                word[++num] = strtok (NULL, " ");
            }

            if(strcmp(word[0], "cd") == 0){
                chdir(word[1]);
                getcwd(cwd, sizeof(cwd));
            }
            else{
                execvp(word[0],word);
            }

            exit(0);
        }
        else{
            wait(&status);
        }
    }

    return 0;
}
odbhut.shei.chhele
  • 5,834
  • 16
  • 69
  • 109
  • 4
    `|` is a shell related token, as far as I know. Doing it outside of a shell doesn't really work because its the shell that reads `|` and makes the magic happen. Without the shell to make the magic piping happen... it doesn't work. – Cornstalks Sep 03 '13 at 23:43
  • SO is there any other way I can run |? can I somehow the string and with brute force make it work – odbhut.shei.chhele Sep 03 '13 at 23:45
  • 3
    @Cornstalks speaks truly. `execvp` is written to execute one command with a set of parameters (if needed). You can try `system` to call a full-blown shell command line. – lurker Sep 03 '13 at 23:45
  • 1
    Before you use `vfork()`, read up on what you're allowed to do in a child process. I don't think `chdir()` or `getpwd()` are allowed. Use `fork()` instead. It probably isn't your main problem (yet), but be very, very wary of the constraints on `vfork()`. – Jonathan Leffler Sep 03 '13 at 23:55
  • See, amongst many possibilities, [C program with pipes to execute `ps aux | grep firefox | tee processes.txt`](http://stackoverflow.com/questions/18582446/c-program-with-pipes-to-excecute-ps-aux-grep-firefox-tee-processes-txt/). – Jonathan Leffler Sep 04 '13 at 00:00
  • Also, I just want to say that I wish I had said "it's" instead of "its," but alas can no longer edit my comment. Just in case anyone's eyes bleed from the typo. – Cornstalks Sep 04 '13 at 00:06
  • Study the source code of existing free software shells like `sash` or `bash` and read [Advanced Linux Programming](http://advancedlinuxprogramming.com/) – Basile Starynkevitch Sep 04 '13 at 05:00

1 Answers1

9

ls -l | less is actually a shell command line that consists of two processes connected by a pipe. The execvp() call can only spawn a single process.

If you want to do this from your program, you must invoke the shell explicitly - either by using the system() call, or by changing the command line to sh -c 'ls -l | less'. Your word array should look like this:

word[0] = "sh"
word[1] = "-c"
word[2] = "ls -l | less"
word[3] = NULL

[EDIT] Alternatively, you could do what the shell is doing internally: spawn two processes and connect them with a pipe. This would involve using the fork(), pipe(), dup2() and execve() calls. However, invoking the shell is much less work, and since less is an interactive program anyway, you don't need to worry about performance that much: anything that takes less than 100 ms is perceived as instantaneous.

Krzysztof Kosiński
  • 4,225
  • 2
  • 18
  • 23