添加以前的作业
This commit is contained in:
259
exp0.5/task52.c
Normal file
259
exp0.5/task52.c
Normal file
@@ -0,0 +1,259 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define MAX_CMD 1024
|
||||
#define MAX_ARGS 64
|
||||
#define MAX_PIPES 32
|
||||
|
||||
char *trim(char *s) {
|
||||
while (isspace((unsigned char)*s)) s++;
|
||||
if (*s == '\0') return s;
|
||||
char *end = s + strlen(s) - 1;
|
||||
while (end > s && isspace((unsigned char)*end)) end--;
|
||||
*(end + 1) = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
int parse_args(char *cmd, char **args) {
|
||||
int argc = 0;
|
||||
char *token = strtok(cmd, " \t");
|
||||
while (token != NULL && argc < MAX_ARGS - 1) {
|
||||
args[argc++] = token;
|
||||
token = strtok(NULL, " \t");
|
||||
}
|
||||
args[argc] = NULL;
|
||||
return argc;
|
||||
}
|
||||
|
||||
int split_pipeline(char *buf, char *segments[], int max_segs) {
|
||||
int nseg = 0;
|
||||
char *seg_start = buf; /* 当前段的起始位置 */
|
||||
char *p = buf;
|
||||
|
||||
while (*p != '\0') {
|
||||
if (*p == '|') {
|
||||
/* 找到管道符:截断当前段,保存去空格后的指针 */
|
||||
*p = '\0';
|
||||
char *seg = trim(seg_start);
|
||||
if (seg[0] != '\0') { /* 跳过空段 */
|
||||
if (nseg >= max_segs) {
|
||||
fprintf(stderr, "shell: too many pipes (max %d)\n", max_segs);
|
||||
return -1;
|
||||
}
|
||||
segments[nseg++] = seg;
|
||||
}
|
||||
seg_start = p + 1; /* 下一段从 '|' 后面开始 */
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
/* 保存最后一段('|' 后面,或整个命令没有 '|' 时) */
|
||||
char *last = trim(seg_start);
|
||||
if (last[0] != '\0') {
|
||||
segments[nseg++] = last;
|
||||
}
|
||||
|
||||
return nseg;
|
||||
}
|
||||
|
||||
static void exec_cmd_in_child(char **args, int argc, int in_fd, int out_fd) {
|
||||
char *infile = NULL;
|
||||
char *outfile = NULL;
|
||||
char *clean_args[MAX_ARGS];
|
||||
int clean_argc = 0;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (strcmp(args[i], "<") == 0 && i + 1 < argc) {
|
||||
infile = args[++i]; /* 记录输入文件,跳过文件名 */
|
||||
} else if (strcmp(args[i], ">") == 0 && i + 1 < argc) {
|
||||
outfile = args[++i]; /* 记录输出文件,跳过文件名 */
|
||||
} else {
|
||||
clean_args[clean_argc++] = args[i]; /* 普通参数保留 */
|
||||
}
|
||||
}
|
||||
clean_args[clean_argc] = NULL;
|
||||
|
||||
if (clean_argc == 0)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (infile) {
|
||||
if (in_fd != STDIN_FILENO)
|
||||
close(in_fd); /* 管道读端不再需要,关掉 */
|
||||
|
||||
int fd = open(infile, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
perror("open"); exit(1);
|
||||
}
|
||||
dup2(fd, STDIN_FILENO);
|
||||
close(fd);
|
||||
} else if (in_fd != STDIN_FILENO) {
|
||||
dup2(in_fd, STDIN_FILENO);
|
||||
close(in_fd);
|
||||
}
|
||||
|
||||
if (outfile) {
|
||||
if (out_fd != STDOUT_FILENO)
|
||||
close(out_fd);
|
||||
int fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd < 0) {
|
||||
perror("open"); exit(1);
|
||||
}
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
close(fd);
|
||||
} else if (out_fd != STDOUT_FILENO) {
|
||||
dup2(out_fd, STDOUT_FILENO);
|
||||
close(out_fd);
|
||||
}
|
||||
|
||||
execvp(clean_args[0], clean_args);
|
||||
fprintf(stderr, "shell: command not found: %s\n", clean_args[0]);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
static void run_single(char *seg) {
|
||||
char buf[MAX_CMD];
|
||||
strncpy(buf, seg, MAX_CMD - 1);
|
||||
buf[MAX_CMD - 1] = '\0';
|
||||
|
||||
char *args[MAX_ARGS];
|
||||
int argc = parse_args(buf, args);
|
||||
if (argc == 0) return;
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) { perror("fork"); return; }
|
||||
if (pid == 0) {
|
||||
/* 子进程:从终端 stdin 读,输出到终端 stdout */
|
||||
exec_cmd_in_child(args, argc, STDIN_FILENO, STDOUT_FILENO);
|
||||
}
|
||||
/* 父进程:等待子进程结束 */
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
static void run_pipeline(char *segments[], int nseg) {
|
||||
int npipes = nseg - 1;
|
||||
|
||||
/* --- 1. 一次性创建所有管道 --- */
|
||||
int pipes[MAX_PIPES][2];
|
||||
for (int i = 0; i < npipes; i++) {
|
||||
if (pipe(pipes[i]) < 0) {
|
||||
perror("pipe");
|
||||
/* 出错:关闭已创建的管道后返回 */
|
||||
for (int j = 0; j < i; j++) {
|
||||
close(pipes[j][0]);
|
||||
close(pipes[j][1]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- 2. 依次 fork 每段命令的子进程 --- */
|
||||
pid_t pids[MAX_PIPES + 1];
|
||||
|
||||
for (int i = 0; i < nseg; i++) {
|
||||
|
||||
/* 确定本段从哪里读、往哪里写:
|
||||
* 第一段:从终端 stdin 读
|
||||
* 中间段:从上一个管道的读端读,向下一个管道的写端写
|
||||
* 最后段:向终端 stdout 写 */
|
||||
int in_fd = (i == 0) ? STDIN_FILENO : pipes[i - 1][0];
|
||||
int out_fd = (i == nseg - 1) ? STDOUT_FILENO : pipes[i][1];
|
||||
|
||||
/* 解析本段命令的参数 */
|
||||
char cbuf[MAX_CMD];
|
||||
strncpy(cbuf, segments[i], MAX_CMD - 1);
|
||||
cbuf[MAX_CMD - 1] = '\0';
|
||||
char *args[MAX_ARGS];
|
||||
int argc = parse_args(cbuf, args);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("fork");
|
||||
/* fork 失败:关闭所有管道,等待已启动的子进程 */
|
||||
for (int j = 0; j < npipes; j++) {
|
||||
close(pipes[j][0]);
|
||||
close(pipes[j][1]);
|
||||
}
|
||||
for (int j = 0; j < i; j++) waitpid(pids[j], NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* 子进程:
|
||||
* 关闭所有"不是自己用"的管道端,
|
||||
* 防止 EOF 永远不到达读端。 */
|
||||
for (int j = 0; j < npipes; j++) {
|
||||
if (pipes[j][0] != in_fd)
|
||||
close(pipes[j][0]);
|
||||
if (pipes[j][1] != out_fd)
|
||||
close(pipes[j][1]);
|
||||
}
|
||||
if (argc == 0)
|
||||
exit(0);
|
||||
exec_cmd_in_child(args, argc, in_fd, out_fd);
|
||||
/* exec_cmd_in_child 内部会 exit,永远不会走到这里 */
|
||||
}
|
||||
|
||||
pids[i] = pid; /* 父进程记录子进程 PID */
|
||||
}
|
||||
|
||||
/* --- 3. 父进程关闭所有管道端,再统一等待子进程 ---
|
||||
* 必须先全部关闭,否则最后一段命令永远读不到 EOF。 */
|
||||
for (int i = 0; i < npipes; i++) {
|
||||
close(pipes[i][0]);
|
||||
close(pipes[i][1]);
|
||||
}
|
||||
for (int i = 0; i < nseg; i++) {
|
||||
waitpid(pids[i], NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void execute(char *cmdline) {
|
||||
char buf[MAX_CMD];
|
||||
strncpy(buf, cmdline, MAX_CMD - 1);
|
||||
buf[MAX_CMD - 1] = '\0';
|
||||
|
||||
char *segments[MAX_PIPES + 1];
|
||||
int nseg = split_pipeline(buf, segments, MAX_PIPES);
|
||||
|
||||
if (nseg <= 0) return; /* 空命令或出错 */
|
||||
|
||||
if (nseg == 1) {
|
||||
run_single(segments[0]); /* 普通命令,无管道 */
|
||||
} else {
|
||||
run_pipeline(segments, nseg); /* 多段管道命令 */
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
char cmdline[MAX_CMD];
|
||||
|
||||
while (1) {
|
||||
printf("%% ");
|
||||
fflush(stdout);
|
||||
|
||||
if (fgets(cmdline, MAX_CMD, stdin) == NULL) {
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
char *cmd = trim(cmdline);
|
||||
if (strlen(cmd) == 0) continue;
|
||||
|
||||
if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "logout") == 0) {
|
||||
printf("Goodbye!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
execute(cmd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user