一、引言
Shell命令解释程序中元字符的处理是shell中一个强大的功能,利用对各种字符的不同解释可以充分挖掘出内核的各种强大的潜能。例如利用“&”符号启动并发的后台进程,利用“<”、“>”符号启动I/O重定向,利用“|”启动管道读写等。同样也可以实现其他一些元字符的功能,如“*”通配符,“;”连接符等。
二、实验内容
问题A
实现一个能处理前后台运行命令的shell
问题B
实现一个带有管道功能的shell
问题C
实现一个能处理I/O重定向的shell
问题D
实现一个能在一行上处理多条命令的shell
三、实验代码
/*****************************************
*
* Shell命令解释系统
*
* Copyright: (C) 2018.4.14 by shaomingshan
*
* Compile: gcc -I/usr/include -lreadline -g -o main main.c
*
* Execute: ./main
* ps aux | sort | uniq > aaa
* cat aaa
*
*****************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <readline/readline.h>
#include <sys/stat.h>
#include <wait.h>
#define LS 1024
#define DELIM "<>;|"
int lineeof(char * str) {
/* 判断命令是否完成输入 */
int len = strlen(str);
while (str[len-] == ) len--;
if (str[len-] == ) {
str[len-] = '\0';
return ;
}
return ;
}
void exec(char * cmd) {
/* 执行命令 */
char *eargv[];
int i = ;
eargv[i] = strtok(cmd, " ");
while (eargv[i++] != NULL) {
eargv[i] = strtok(NULL, " ");
}
eargv[i] = ;
execvp(eargv[], eargv);
}
char * trim(char *str) {
/* 去前后空白 */
int l = ;
int r = strlen(str)-;
while (str[l] == ' ') l++;
while (str[r] == ' ') r--;
str[r+] = '\0';
return &str[l];
}
void next_cmd(char *str, int *i) {
/* 找到下一个分命令开始地方 */
for (; *i < strlen(str); ++*i) {
if (str[*i+]==';') break;
}
}
int main() {
char *line, *temp, *raw, *flow;
char *filein, *fileout, *cmd;
char nil;
int i, argc, status, in_arg, jobs=;
int is_bkg, is_pip, k=;
pid_t pid[], bpid[];
char *argv[LS/];
char *fifo[] = {"fifo0", "fifo1", "fifo2"};
raw = (char *) malloc(LS);
line = (char *) malloc(LS);
temp = (char *) malloc(LS);
flow = (char *) malloc(LS/);
cmd = NULL; // 存放命令主体
filein = NULL; // 重定向输入
fileout = NULL; // 重定向输出
for (i=;i<;++i) {
unlink(fifo[i]);
mkfifo(fifo[i], );
}
while () {
raw = trim(readline("$> "));
if (!strlen(raw)) continue; // 空行
if (!strcmp(raw,"exit")) break; // exit命令
while (!lineeof(raw)) {
// 命令是否结束,"\"符号
raw = strcat(raw, readline(" > "));
}
memset(line, , LS);
memcpy(line, raw, strlen(raw));
argc = ;
is_pip = ;
in_arg = -;
temp = strtok(line, DELIM);
while (temp != NULL) {
in_arg += strlen(temp)+;
flow[argc] = raw[in_arg];
argv[argc++] = trim(temp);
temp = strtok(NULL, DELIM);
}
flow[argc-] = ';';
flow[argc] = '\0';
for (i = ; i < strlen(flow); ++i) {
switch (flow[i]) {
case ';':
// 判断是否后台
nil = argv[i][strlen(argv[i])-];
is_bkg = nil=='&'? : ;
case '|':
cmd = cmd? cmd : argv[i]; // 存储命令
// exec无法处理cd命令,只能单独处理
if (strstr(cmd, "cd")==cmd) {
// 字符串首为cd
if (strlen(cmd)==) {
// HOME
chdir("/home/fio");
} else if (cmd[]==' ') {
// 且有参数,命令是trim过的
char * dir = &cmd[];
chdir(dir);
}
} else {
pid[k] = fork();
if (pid[k] == ) {
// input 管道输入优先于重定向来源
if (is_pip) {
stdin = freopen(fifo[k], "r", stdin);
} else if (filein) {
stdin = freopen(filein, "r", stdin);
}
// output 重定向文件优先于管道
k = (k+)%; // 轮转一下
if (fileout) {
stdout = freopen(fileout, "w", stdout);
if (flow[i]=='|') {
// ls > b | wc
// 需要打开管道再关闭
int fd = open(fifo[k], O_WRONLY);
close(fd);
}
} else if (flow[i]=='|') {
stdout = freopen(fifo[k], "w", stdout);
}
exec(cmd); // 执行命令
} else {
if (is_pip) {
// 回收,如果当前是管道则不回收,交给下一个子进程执行再回收
waitpid(pid[(k+)%], &status, );
if (status != ) {
printf("error: exit with code %d[%d]\n", status, pid[(k+)%]);
// 跳到下一个分命令
next_cmd(flow, &i);
}
}
if (!is_bkg && (flow[i]!='|'||fileout)) {
// 回收前一个管道命令子进程
waitpid(pid[k], &status, );
if (status != ) {
printf("Error: exit with code %d[%d]\n", status, pid[k]);
next_cmd(flow, &i);
}
} else if (is_bkg) {
bpid[jobs++] = pid[k]; // 存放后台命令,未完全实现
}
is_pip = flow[i] == '|'? :;
k = (k+)%;
cmd = NULL;
filein = NULL;
fileout = NULL;
}
}
break;
case '>':
cmd = cmd ? cmd : argv[i];
fileout = argv[i+];
break;
case '<':
if (!cmd) cmd = argv[i];
filein = argv[i+];
break;
default:
printf("Syntax Error\n");
exit(-);
}
}
}
}
四、运行结果
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcpXRE1UMRRUTw0keYFjUXFGaKJDTwYVbiVHNHpleO1GTulzRilWO5x0LcRHelR3LcJzLctmch1mclRXY39zN2ADMwgzMxEDMzQDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
如有错误请指正