要求:
解释执行命令,支持输入输出重定向,支持管道,后台运行程序。
/*************************************************************************
> File Name: shell.c
> Author:wyf
> Mail:Catherine199787@outlook.com
> Created Time: 2016年07月30日 星期六 10时54分55秒
************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<dirent.h>
#include<string.h>
#include<malloc.h>
#define normal 0
#define out 1
#define in 2
#define pipe 3
//自定义错误函数
void err(char* err_string, int line){
perror(err_string);
fprintf(stderr, "line:%d", line);
return;
}
//判断命名的正确性
int right(char *arg){
DIR *dir;
struct dirent* ptr;
char *path[] = {"./","/bin","/usr/bin/",NULL}; //环境变量PATH的路径
int i;
if(strncmp(arg, "./", 2) == 0){ //将路径改为当前目录
arg = arg + 2; //指针移到/下一个字节
}
for(i = 0; path[i] != NULL; i++){ //查找命令
dir = opendir(path[i]);
if(dir == NULL){
err("open_bin error", __LINE__);
}
while((ptr = readdir(dir)) != NULL){
if(strcmp(ptr->d_name, arg) == 0){
closedir(dir);
return 1;
}
}
closedir(dir);
}
return 0;
}
//管道的执行函数
void argpipe(char *argn[], char *arg[]){
int pid;
int status;
int fd;
pid = fork();
if(pid < 0){
err("pipe_fork", __LINE__);
}
else if(pid == 0){
if(!right(arg[0])){
err("pipe arg", __LINE__);
}
fd = open("/tmp/youdonotknowfile", O_WRONLY | O_CREAT | O_TRUNC,0644);
dup2(fd, 1);
execvp(arg[0], arg);
exit(0);
}
if(waitpid(pid, &status, 0) == -1){
err("pipe_wait", __LINE__);
}
if(!right(argn[0])){
err("pipe_arg", __LINE__);
}
fd = open("/tmp/youdonotknowfile", O_RDONLY);
dup2(fd, 0);
execvp(argn[0], argn);
if(remove("/tmp/youdonotknowfile")){
err("remove", __LINE__);
}
}
//输入函数
void arginput(char *buf){
int len = 0;
gets(buf);
len = strlen(buf);
if(len >= 255){
err("arginput", __LINE__);
}
}
//解析输入的命令
void argexplain(char * buf, char arglist[][255], int *argcount)
{
int number = 0;
int i = 0;
while(1){
if(buf[i] == '\0'){
break;
}
else if(buf[i] != ' '){
arglist[*argcount][number] = buf[i];
number++;
}
else{
arglist[*argcount][number] = '\0';
number = 0;
*argcount = *argcount + 1; //*argcount 为下标
}
i++;
}
}
//执行输入
void argdo(int argcount, char arglist[][255]){
char *arg[argcount+1];
int wrong = 0;
int how = 0;
int background = 0;
int fd;
int i;
int status;
char path[100]; //各种无法清空path的缓存,有八阿哥。
char *argn[argcount+1];
pid_t pid;
for(i = 0; i <= argcount ; i++){ //准备execvp的第二个参数
arg[i] = arglist[i];
}
arg[i] = NULL;
for(i = 0; i <= argcount; i++){
if(strcmp(arg[i], "&") == 0){
if(i == (argcount - 1)){
background = 1;
arg[argcount-1] = NULL;
break;
}
else{
err("too many '&'", __LINE__);
return;
}
}
}
for(i = 0; arg[i] != NULL; i++){
if(strcmp(arg[i], ">") == 0){
wrong++;
how = out;
if(arg[i+1] == NULL){
wrong++;
}
}
else if(strcmp(arg[i], "<") == 0){
wrong++;
how = in;
if(arg[i+1] == NULL){
wrong++;
}
}
else if(strcmp(arg[i], "|") == 0){
wrong++;
how = pipe;
if(arg[i+1] == NULL){
wrong++;
}
else if(i == 0){
wrong++;
}
}
}
if(wrong > 1){
err("too many arg", __LINE__);
}
pid = fork();
if(pid < 0){
err("fork error", __LINE__);
}
switch(how){
case 0:{
if(pid == 0){
if( !right(arg[0]) ){
// perror("wrong arg");
exit(0);
}
execvp(arg[0], arg);
exit(0);
}
}
break;
case 1:{
for(i = 0; arg[i] != NULL; i++){
if(strcmp(arg[i], ">") == 0){
strcpy(path, arg[i+1]);
arg[i] = NULL;
}
}
if(pid == 0){
if( !right(arg[0]) ){
err("wrong arg", __LINE__);
}
printf("%s\n",path);
fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
if(fd < 0){
err("open faild", __LINE__);
}
dup2(fd, 1); //通过dup2实现标准输出
execvp(arg[0], arg);
}
}
break;
case 2:{
for(i = 0; arg[i] != NULL; i++){
if(strcmp(arg[i], "<") == 0){
strcpy(path, arg[i+1]);
arg[i] = NULL;
}
}
if(pid == 0){
if( !right(arg[0]) ){
err("wrong arg", __LINE__);
}
fd = open(path, O_RDONLY);
if(fd < 0){
err("open faild", __LINE__);
}
dup2(fd, 0);
execvp(arg[0], arg);
exit(0);
}
}
break;
case 3:{
for(i = 0; arg[i] != NULL; i++){
if(strcmp(arg[i], "|") == 0){
arg[i] = NULL;
int j;
for(j = i+1; arg[j] != NULL; j++){ //管道命令数组
argn[j-i-1] = arg[j];
}
argn[j-1-i] = NULL;
break;
}
}
if(pid == 0){
argpipe(argn, arg);
}
}
break;
default:
break;
}//switch
if(background == 1){ //后台运行父进程不管子进程直接返回
return ;
}
if(waitpid(pid, &status, 0) == -1){ //他爸等他娃结束
err("wait for chil error", __LINE__);
}
}
/******************主函数***********************/
int main(int argc,char *argv[]){
char *buf;
int argcount = 0, i;
char arglist[100][255];
char **arg = NULL;
buf = (char *)malloc(255);
if(buf == NULL){
err("malloc", __LINE__);
}
while(1){
memset(buf, 0 ,256);
printf("my_shell$$");
arginput(buf);
if(strcmp(buf, "exit") == 0 || strcmp(buf, "logout") == 0)
{
break;
}
for(i = 0; i < 100; i++){
arglist[i][0] = '\0';
}
argcount = 0;
argexplain(buf, arglist, &argcount);
argdo(argcount, arglist);
}
if(buf != NULL){
free(buf);
buf = NULL;
}
return 0;
}
总结:
参考了课本,对于重定向,管道的概念不是很清晰,程序的布局把握的不好,二级指针的运用不是很熟练。
问题:
很多。