外观
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;
}