project需要,所以做了一个。参考了网站:http://stephen-brennan.com/2015/01/16/write-a-shell-in-c/
实现了:执行基本命令,从PROFILE文件读取初始working directory,进程持续5秒以后可以提示杀死进程,以及一个alias功能(时间不够只写了逻辑部分,如果读写没问题的话应该能够实现),以及一个if 的条件的解析(做的不太好,日后补上)
代码:
//
// main.c
// Shell_c
//
// Created by liuchang on 9/18/15.
// Copyright (c) 2015 liuchang. All rights reserved.
//
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <string.h>
void show_prompt();
char * read_line();
int exec_command(char**);
char ** split_line(char *);
int launch(char ** args);
int is_fileexist(char * comm);//check if exists the command
char buffer[80];//缓存用户输入的命令
#define BUFFSIZE 500
#define TRUE 1
int command_length=0;
//builtin function
int builtin_exit(char ** args);
int builtin_cd(char ** args);
int builtin_if(char ** args);
int builtin_alias(char ** args);
int builtin_help(char ** args);
int num_builtin();
int check_table(char * alias);// check if the alias exist in the table
char * builtin_str[]={"exit","cd","if","alias","help"};
int (*builtin_func[])(char **) = {&builtin_exit,&builtin_cd,&builtin_if,&builtin_alias,&builtin_help};
int main(int argc, const char * argv[]) {
//prepare
time_t nowtime;
struct tm *timeinfo;
time( &nowtime );
timeinfo = localtime( &nowtime );
int year, month, day;
year = timeinfo->tm_year + 1900;
month = timeinfo->tm_mon + 1;
day = timeinfo->tm_mday;
printf("welcome to my shell\n");
printf("%d-%d-%d\n", year, month, day);
//read file,switch workspace
FILE * file;
int i;
char buf[BUFFSIZE];
memset(buf,0,sizeof(buf));
if((file = fopen("PROFILE","r")) == NULL){
printf(" cannot open file, terminate immediately\n");
exit(1);
}
i=0;
while(!feof(file)){
buf[i++] = fgetc(file);//??? what the fuck
if(i>50){
printf("buffer is not enough\n");
exit(1);
}
}
buf[i-2]='\0';//-2??
i--;
char * user=getlogin();
int count =0;
while(count < sizeof(user)){
buf[i++] = user[count++];
}
//change directory
chdir(buf);
//printf("the user is : %s\n",user);
//printf("the directory is: %s ",buf);
//main loop
char * command;
char ** args;
int status=1;
while (status) {
command_length=0;
//show propmpt
show_prompt();
//read line
command =read_line();
//printf("the input is : %s",command);
//split command
args=split_line(command);
//execute in a child process
status=exec_command(args);
free(command);
free(args);
}
}
void show_prompt(){
//add user info
char * path;
char *user;
path = get_current_dir_name();
user=getlogin();
printf("%s@%s$",user, path);
}
char * read_line(void){
//char *input=NULL;//用户输入命令
//int command_len=0;//输入字符个数
char *line = NULL;
size_t bufsize = 0; // have getline allocate a buffer for us
getline(&line, &bufsize, stdin);
return line;
}
int exec_command(char ** args){
//printf("executing command..\n");
//check several conditions..
if(args[0]==NULL){
return 1;// 比如按下 enter
}
//if it is builtin command..
int i;
//printf("the number of built in: %d",num_builtin());
for(i =0;i<num_builtin();i++){
if(strcmp(args[0],builtin_str[i]) ==0){
//printf("call builtin function\n");
return (*builtin_func[i])(args);
}
}
//else
//printf("using noral function\n");
return launch(args);
}
#define BUFFER_TOK_SIZE 64 // 节约空间的考虑?
#define TOK_DELIM " \t\r\n\a" //了解下分隔符的意思
char ** split_line(char * command){
//用函数截取
int buffsize = BUFFER_TOK_SIZE,position=0;
char ** tokens = malloc(buffsize * sizeof(char*));//??
char * token;
if (!tokens) {
fprintf(stderr, "allocation failed\n");
exit(EXIT_FAILURE);
}
token = strtok(command,TOK_DELIM);
command_length++;
while (token!=NULL) {
tokens[position]=token;
position++;
//如果超出缓冲区大小,增加缓冲区,使用realloc
if(position>buffsize){
buffsize +=BUFFER_TOK_SIZE;
tokens = realloc(tokens, buffsize * sizeof(char*));
if(!tokens){
fprintf(stderr, "allocate error\n");
exit(EXIT_FAILURE);
}
}
token = strtok(NULL, TOK_DELIM);
command_length++;
}
tokens[position]=NULL;
//int i;
// for ( i=0; i<sizeof(tokens);i++) {
// printf("command: %d, %s \n",i,tokens[i]);
// }
return tokens;
}
int launch(char ** args){
if(is_fileexist(args[0])==-1){
printf("%s:command not found\n",args[0]);
//exit(EXIT_FAILURE);
return 1;
}
pid_t pid,wpid,pid_p;
int status;
pid_p = getpid();
//printf("parend pid %d \n",pid_p);
pid = fork();
if(pid<0){
perror("fork failed");
}else if (pid ==0){
//child
if (execvp(args[0], args) == -1) {
printf("No such file or directory\n");
int error_code = errno;
//printf("error code is %d: ",error_code);
}
exit(EXIT_FAILURE);
}else{
// Parent process
clock_t start,wait;
start = clock();
pid_t pid_counter;
pid_counter = fork();
if(pid_counter<0){
perror("fork failed");
}else if(pid_counter == 0){
//child
while(1){
wait = clock();
double duration = wait-start;
if(duration/CLOCKS_PER_SEC>5){
//hint
char * answer;
while(1){
printf("do you want to kill the child process?(y/n)\n");
answer =read_line();
if(answer[0]=='y'){
kill(pid,SIGABRT);//type of signal
printf("killed the chiled process because it last over five seconds\n");
break;
}else if(answer[0]=='n'){
break;
}
else{
printf("please enter y or n\n");
}
}
//send signal pid_p
//printf("duration: %f",duration/CLOCKS_PER_SEC);
return(EXIT_SUCCESS);
}
}
}
wpid = waitpid(pid, &status, WUNTRACED);
//wpid = waitpid(pid_counter, &status, WUNTRACED);
//printf("retrieve the child process\n");
kill(pid_counter,SIGABRT);
}
return 1;
}
int is_fileexist(char * comm)//查找命令是否存在
{
char * env_path, * p;
int i=0;
/* 使用getenv函数来获取系统环境变量,用参数PATH表示获取路径*/
env_path = getenv("PATH");
p = env_path;
while(*p != '\0') {
/* 路径列表使用":"来分隔路径*/
if(*p != ':') buffer[i ++] = *p;
else {
buffer[i ++] = '/';
buffer[i] = '\0';
/* 将指令和路径合成,形成pathname,并使用access函数来判断该文件是否存在*/
strcat(buffer, comm);
if(access(buffer, F_OK) == 0) /* 文件被找到*/
return 0;
else /* 继续寻找其他路径*/
i = 0;
}
p ++;
}
/* 搜索完所有路径,依然没有找到则返回 –1*/
return -1;
}
//built in funcion related
int num_builtin(){
return sizeof(builtin_str)/ sizeof(char *);//very painful
}
int builtin_exit(char ** args){
printf("bye-bye~\n");
return 0;
}
int builtin_cd(char ** args){
if (args[1] == NULL) {
fprintf(stderr, "expected argument to \"cd\"\n");
} else {
if (chdir(args[1]) != 0) {
perror("chdir failed\n");
}
}
return 1;
}
int builtin_if(char ** args){
//parse if command1; then command2; else command3 fi
int i;
//printf("commands are:\n");
int arr[] = {0,0,0};// then, else,fi
int pos_then = 0;
int pos_else = 0;
for(i = 0;i<command_length-1;i++){
//printf(" %s ",args[i]);
if(strcmp("then",args[i])==0) {arr[0]=TRUE;pos_then=i;}
if(strcmp("else",args[i])==0) {
pos_else = i;
arr[1]=TRUE;//just in case we need to run another process
}
if(i==command_length-2){
if(strcmp("fi",args[i])==0) arr[2]=TRUE;
}
}
//printf("\n");
int j;
//for(j=0;j<3;j++) printf("array %d, %d ",j,arr[j]);
//printf("\n args before then:%s \n",args[pos_then-1]);
if( (strcmp(args[pos_then-1],";")==0) && arr[0] && arr[2]){
//printf("valid if expression\n");
//split: command 1 , command 2 , command 3
//command1
int buffsize = BUFFER_TOK_SIZE,position=0;
char ** tokens = malloc(buffsize * sizeof(char*));
int x;
for(x =1;x<pos_then-1;x++){
tokens[position++] = args[x];
//printf("command 1 : %s\n ",args[x]);
}
//printf("token %s",tokens[1]);
int res = launch_if(tokens);
//printf("result for command 1:%d \n",res);
//command 2
position=0;
tokens = malloc(buffsize * sizeof(char*));
//printf("command length: %d , pos_then: %d \n",command_length,pos_then);
if(!arr[1]){
for(x =pos_then+1;x<command_length-2;x++){
tokens[position++] = args[x];
//printf("command 2 : %s\n ",args[x]);
}
}else {
for(x =pos_then+1;x<pos_else;x++){
tokens[position++] = args[x];
//printf("command 2 : %s\n ",args[x]);
}
}
if(res){ // just command 2
//launch command 2
launch(tokens);
}else{
//if exist, launch command3
if(arr[1]){
//split command3
position=0;
tokens = malloc(buffsize * sizeof(char*));
int y;
for(y =pos_else+1;y<command_length-2;y++){
tokens[position++] = args[y];
//printf("command 3 : %s\n ",args[y]);
}
//launch command 3
launch_if(tokens);
}
}
}else{
printf("invalid expression, use like this:$ if command1 ; then command2 ; else command3 fi \n");
return 1;
}
}
int launch_if(char ** args){
if(is_fileexist(args[0])==-1){
printf("%s:command not found\n",args[0]);
//exit(EXIT_FAILURE);
return 0;
}
pid_t pid,wpid,pid_p;
int status;
pid_p = getpid();
//printf("parend pid %d \n",pid_p);
pid = fork();
if(pid<0){
perror("fork failed");
}else if (pid ==0){
//child
if (execvp(args[0], args) == -1) {
printf("No such file or directory\n");
int error_code = errno;
//printf("error code is %d: ",error_code);
return 0;
}
//exit(EXIT_FAILURE);
return 1;
}else{
// Parent process
clock_t start,wait;
start = clock();
pid_t pid_counter;
wpid = waitpid(pid, &status, WUNTRACED);
//wpid = waitpid(pid_counter, &status, WUNTRACED);
//printf("retrieve the child process\n");
}
return 1;
}
int builtin_alias(char ** args){
//printf("builtin func alias\n");
int j;
//for(j=0;j<command_length-1;j++){
// printf("token: %s ",args[j]);
// }
//check if the alias is not exist in the alias table
int res01;
res01=check_table(args[2]);
//printf("whether the alias exist no not: %d \n",res01);
//check if the alias is a command
int res02;
res02 = is_fileexist(args[2]);// -1 not exist; 0 exist
//printf("whether the alias is a command: %d \n",res02);
//check if the command is a existed command
int res03;
res03 = is_fileexist(args[1]);
//printf("whether the command is a existed command %d \n ",res03);
if(res01) { printf("the alias:%s is already existed ...\n ",args[command_length-2]); return 1;}
if(res02 == 0) { printf("the alias:%s itself is a command \n",args[command_length-2]); return 1;}
if(res03 == -1){ printf("the command:%s not found\n",args[1]); return 1;}
//save the command and alias to hardware
char * command;
command= (char*)malloc(BUFFSIZE);
//copy
int t;
for(t=1;t<command_length-2;t++) strcat(command,args[t]);
//printf("the command is :%s \n",command);
printf("the alias has been saved\n");
return 1;
}
int check_table(char * alias){
//check if the alias exist
return 0;//not existed
}
int builtin_help(char ** args){
printf("there are some helpful hints:\n the builtin functions are as follows\n");
int i;
for (i = 0; i < num_builtin(); i++) {
printf(" %s ", builtin_str[i]);
}
printf("\nfor other hints please check the document.\n");
}