Step 4: Run programs with fork and exec
To run another program the shell uses a classic Unix trick. fork makes a copy of the shell (the child). In the child, exec replaces the copy with the new program. The parent calls waitpid and learns the child's exit status: 0 means success, anything else is a failure (127 by convention means "command not found"). Note the fflush(stdout) before fork: without it, text waiting in the output buffer would be duplicated into the child and printed twice.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_ARGS 16
int tokenize(char *line, char *argv[]) {
int argc = 0;
char *p = line;
while (*p != '\0' && argc < MAX_ARGS - 1) {
while (*p == ' ') p++;
if (*p == '\0') break;
argv[argc++] = p;
while (*p != '\0' && *p != ' ') p++;
if (*p != '\0') *p++ = '\0';
}
argv[argc] = NULL;
return argc;
}
// Run a program: fork a child, replace it with the program (exec), and wait for it to finish.
int run_external(char *argv[]) {
fflush(stdout); // otherwise buffered text would be printed twice
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return 1;
}
if (pid == 0) { // the child
execvp(argv[0], argv);
printf("tiny: command not found: %s\n", argv[0]);
fflush(stdout);
_exit(127); // exec only returns if it failed
}
int status;
waitpid(pid, &status, 0); // the parent waits
return WIFEXITED(status) ? WEXITSTATUS(status) : 1;
}
int main(void) {
char *script[] = { "echo hello from a child process", "true", "false", "nosuchprogram --flag", "echo one two three" };
char *argv[MAX_ARGS];
for (int i = 0; i < 5; i++) {
char line[100];
strcpy(line, script[i]);
printf("tiny> %s\n", line);
int argc = tokenize(line, argv);
if (argc == 0) continue;
int status = run_external(argv);
printf("[exit status %d]\n", status);
}
return 0;
}tiny> echo hello from a child process hello from a child process [exit status 0] tiny> true [exit status 0] tiny> false [exit status 1] tiny> nosuchprogram --flag tiny: command not found: nosuchprogram [exit status 127] tiny> echo one two three one two three [exit status 0]
true exits 0 and false exits 1, which is exactly what scripts test with if. A program that does not exist makes execvp return, so the child prints a message and exits with 127.
What does a process exit status of 0 mean?
Finish the quick check (and any code exercise) to complete this lesson, or mark it done yourself.
Tip: use the left and right arrow keys to move between lessons.