Skip to content
Advertisement

Why is execvp() executing twice using fork()?

I am implementing a shell.

When attempting a command other than changing directories, execvp() runs, the child terminates and a new child is created. When I change directories, the child does not terminate and a new child is created. Here is a sample of my code:

for(;;) {
    printf("bash: ");
    parse();
    ...
    pid_t pid = fork()
    if (pid == 0)
        if (!strcmp(line[0], "cd"))
            if (!line[1]) (void) chdir(getenv("HOME"));
            else (void) chdir(line[1]);
        else execvp(line[0], line);
    ...
    if (pid > 0) {
        while (pid == wait(NULL));
        printf("%d terminated.n", pid);
    }
}

cd ../; ls; runs correctly, except I have to Ctrl+D twice to end the program.

Though, if I pipe the same information (ie. mybash < chdirtest), it runs correctly once, terminates the child, runs again except in the original directly, then terminates the final child.

Advertisement

Answer

cd should not be invoked through a child process, the shell itself should change its current directory (that’s the property of an internal command: modify the process of the shell itself).

A (primitve) shell should looks like:

for(;;) {
    printf("bash: ");
    parse();

    // realize internal commands (here "cd")
    if (!strcmp(line[0], "cd")) {
       if (!line[1]) (void) chdir(getenv("HOME"));
       else (void) chdir(line[1]);
       continue; // jump back to read another command
    }

    // realize external commands
    pid_t pid = fork()
    if (pid == 0) {
        execvp(line[0], line);
        exit(EXIT_FAILURE); // wrong exec
    }

    // synchro on child
    if (pid > 0) {
        while (pid == wait(NULL));
        printf("%d terminated.n", pid);
    }
}
Advertisement