Skip to content

csapp/tiny_shell.c ​

配套源码,运行方法见同目录 README。返回实验总览。

c
/* Foreground-only teaching shell. Tokenizes whitespace, no quoting/job control. */
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_ARGS 64

int main(void) {
    char *line = NULL;
    size_t capacity = 0;
    struct sigaction ignore = {0}, original;
    ignore.sa_handler = SIG_IGN;
    sigemptyset(&ignore.sa_mask);
    if (sigaction(SIGINT, &ignore, &original) < 0) { perror("sigaction"); return 1; }
    while (1) {
        if (isatty(STDIN_FILENO)) { fputs("study> ", stdout); fflush(stdout); }
        if (getline(&line, &capacity, stdin) < 0) break;
        char *arguments[MAX_ARGS], *save;
        size_t count = 0;
        char *token = strtok_r(line, " \t\r\n", &save);
        while (token && count < MAX_ARGS - 1) {
            arguments[count++] = token;
            token = strtok_r(NULL, " \t\r\n", &save);
        }
        if (token) { fputs("too many arguments\n", stderr); continue; }
        arguments[count] = NULL;
        if (!count) continue;
        if (strcmp(arguments[0], "exit") == 0) break;
        if (strcmp(arguments[0], "cd") == 0) {
            if (count != 2) fputs("usage: cd directory\n", stderr);
            else if (chdir(arguments[1]) < 0) perror("cd");
            continue;
        }
        pid_t child = fork();
        if (child < 0) { perror("fork"); continue; }
        if (child == 0) {
            /* SIG_IGN survives exec; restore default so Ctrl-C reaches command. */
            struct sigaction defaults = {0};
            defaults.sa_handler = SIG_DFL;
            sigemptyset(&defaults.sa_mask);
            if (sigaction(SIGINT, &defaults, NULL) < 0) _exit(126);
            execvp(arguments[0], arguments);
            perror(arguments[0]);
            _exit(127);
        }
        int status;
        pid_t result;
        do { result = waitpid(child, &status, 0); } while (result < 0 && errno == EINTR);
        if (result < 0) perror("waitpid");
        else if (WIFSIGNALED(status)) fprintf(stderr, "terminated by signal %d\n", WTERMSIG(status));
        else if (WEXITSTATUS(status)) fprintf(stderr, "exit status %d\n", WEXITSTATUS(status));
    }
    free(line);
    if (sigaction(SIGINT, &original, NULL) < 0) return 1;
    return ferror(stdin) ? 1 : 0;
}