首先设置参数
最多命令参数 MAX_CMD
和每个命令的最大长度 MAX_LEN
#define MAX_LEN 32
#define MAX_CMD 20
#define QUIT "quit"
使用一个二维数组来保存从终端得到的命令,并且使用两个int数组(start,end)来对每组命令进行划分(按照 “|”)。其中在cmd中使用’\0’来作为命令的结束。
首先分配内存,因为函数传参是值语义,所以需要将其地址传入。
void initilize(char **buf, char ***cmd, int **start, int **end){
*buf = (char*)malloc(sizeof(char) * 1024);
*cmd = (char**)malloc(sizeof(char*) * MAX_CMD);
*start = (int*)malloc(sizeof(int) * MAX_CMD);
*end = (int*)malloc(sizeof(int) * MAX_CMD);
int i = 0;
for(; i < MAX_CMD; ++i){
*((*cmd) + i) = (char*)malloc(sizeof(char) * MAX_LEN);
}
}
获取命令。首先将终端数据读取到buf中,在使用strtok函数,以空格为分隔符对buf进行分割,得到命令。
int getCom(char* buf, char **cmd){
char *s;
int i;
write(STDOUT_FILENO, "yyd # ", 6);
//bzero(buf, 1024);
i = read(STDIN_FILENO, buf, 1024);
buf[i - 1] = '\0';
i = 0;
s = strtok(buf, " ");
while(s){
if(!strcmp(s, QUIT)) return -1;
strcpy(*(cmd + (i++)), s);
s = strtok(NULL, " ");
}
**(cmd + i) = '\0';
return 0;
}
对得到的命令按照“|”进行拆分。每一部分使用start和end来保存
int partCom(char **cmd, int *start, int *end){
int i = 0;
int nums = 1;
*(start++) = 0;
while(**(cmd + i) != '\0'){
// printf("cmd %s \n",*(cmd + i));
if(!strcmp(*(cmd + i), "|")){
*(end++) = i - 1;
*(start++) = ++i;
nums++;
}else{
i++;
//printf("fuck %d\n", i);
}
}
*(end++) = i - 1;
*(start) = *(end) = -1;
return nums;
}
执行。其中nums是命令组的个数。首先判断是一个命令还是多个命令。多个需要使用递归创建管道。
如果是一个的话。先将命令通过start和end从cmd中获得保存到tmp中,在获取pathname。然后检查是否命令中包含重定向(check_redirect),如果返回-1则说明没有,可以直接execvp。如果有则交给rediret_handle处理。
void execute(char **cmd, int *start, int *end, int nums){//cmd nums
if(nums == 1){
char *tmp[MAX_CMD];
char pathname[MAX_LEN];
int i = 0;
int type;
int s = *start;
int e = *end;
if(s > e) return;
while(s != e) tmp[i++] = *(cmd + s++);
tmp[i] = *(cmd + *end);
tmp[++i] = NULL;
if(fork()){
waitpid(-1, NULL, 0);
return;
}else{
strcpy(pathname, "/bin/");
strcat(pathname, *(cmd + *start));
if((type = check_redirect(tmp, &i)) == -1) execvp(pathname, tmp);
redirect_handle(tmp, type, i);
_exit(0);
}
}
if(fork()){
waitpid(-1, NULL, 0);
return;
}else{
re_execute(cmd, start + nums - 1, end + nums - 1, 1, nums);
_exit(0);
}
}
一共六种重定向,其中i表示第几个字符串是重定向标志。
int check_redirect(char *list[], int *i){// > < >> << >& &<
char **s = list;
int type = -1;
*i = -1;
while(*s){
if(!strcmp(*s, ">")) type = 0;
else if(!strcmp(*s, "<")) type = 1;
else if(!strcmp(*s, ">>")) type = 2;
else if(!strcmp(*s, ">>")) type = 3;
else if(!strcmp(*s, ">&")) type = 4;
else if(!strcmp(*s, "&<")) type = 5;
if(type != -1){
(*i)++;
return type;
}
(*i)++;
s++;
}
return type;
}
void (*redirect_ptr[])(char *list[], int p) = {redirect_0, redirect_1, redirect_2,
redirect_3, redirect_4, redirect_5};
void redirect_handle(char* list[] , int type, int p){
redirect_ptr[type](list, p);
}
void redirect_0(char *list[], int p){
_redirect_help(list, p, 0);
}
IO重定向中>>,<<是往文件末尾添加数据O_APPRND, > < 是写入,并且会截断原文件O_TRUNC。>& &<是像文件描述符中写入。
通过fork()创建子进程,使用管道连接父子进程,传递数据。将对子进程的管道输入端pfd[1]重定向到标准输出1,将父进程的管道输出端pfd[0]定向到标准输入0。
注意,要在父子进程关闭不用的管道描述符,否则会看不到消息的边界,一直阻塞。
void _redirect_help(char *list[], int p, int type){
char *file;
char *par[MAX_LEN];
char *cmd;
int pos;
int fd = -1;
int flag = O_CREAT|O_RDWR|O_TRUNC;
list[p] = NULL;
switch(type){
case 2: flag |= ~O_TRUNC; flag &= O_APPEND; // >>
case 0: file = list[p + 1]; cmd = list[0]; pos = 0; break; // >
case 3: flag |= ~O_TRUNC; flag &= O_APPEND; // <<
case 1: file = list[0]; cmd = list[p + 1]; pos = p + 1; break; // <
case 4: fd = atoi(list[p + 1]); cmd = list[0]; pos = 0; break; // >&
case 5: fd = atoi(list[0]); cmd = list[p + 1]; pos = p + 1; break;
}
int i = 0;
while(list[pos] != NULL){
par[i++] = list[pos++];
}
par[i] = NULL;
int pfd[2];
char buf[1024];
if(fd == -1) fd = open(file, O_CREAT|O_RDWR|O_TRUNC, 0666);
pipe(pfd);
if(fork()){
int i;
close(pfd[1]);
close(0);
dup2(pfd[0], 0);
while((i = read(pfd[0], buf, 1024)) > 0 ){
write(fd, buf, i);
}
close(fd);
}else{
close(pfd[0]);
close(1);//stdout
dup2(pfd[1], 1);
execvp(cmd, par);
}
}
多个管道相连时使用递归创建。
C最先执行,然后P2等待返回,继续执行,然后P1,P0。所以应该将命令倒输入。
execute中。首先创建一个子进程,在子进程中进行递归处理。其中参数fd是连接进程的管道描述符pfd[1]。
if(fork()){
waitpid(-1, NULL, 0);
return;
}else{
re_execute(cmd, start + nums - 1, end + nums - 1, 1, nums);
_exit(0);
}
}
void re_execute(char **cmd, int *start, int *end, int fd, int nums){
char *tmp[MAX_CMD];
char pathname[MAX_LEN];
int i = 0;
int type;
int status;
int s = *start;
int e = *end;
while(s != e){
tmp[i++] = *(cmd + s);
s++;
}
tmp[i++] = *(cmd + e);
tmp[i] = NULL;
strcpy(pathname, "/bin/");
strcat(pathname, *(cmd + *start));
if(fd != 1){
close(1);
dup2(fd, 1);
}
if(nums == 1){
if((type = check_redirect(tmp, &i)) == -1) execvp(pathname, tmp);
redirect_handle(tmp, type, i);
}
int pfd[2];
pipe(pfd);
if(fork()){
close(0);
close(pfd[1]);
dup2(pfd[0], 0);
waitpid(-1, &status, 0);
if((type = check_redirect(tmp, &i)) == -1) execvp(pathname, tmp);
}else{
close(pfd[0]);
re_execute(cmd, start - 1, end - 1, pfd[1], nums - 1);
_exit(0);
}
}
运行结果