栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

操作系统真象还原实验记录之实验三十一:实现简单的shell

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

操作系统真象还原实验记录之实验三十一:实现简单的shell

操作系统真象还原实验记录之实验三十一:实现简单的shell

Windows中,图形界面的资源管理器和命令行窗口都是交互接口,尽管这些交互接口名字及外观各异,但他们往往统称外壳程序

shell的功能是获取用户的输入,分析输入的命令,判断内部命令还是外部命令,然后执行不同的命令策略

1.简单shell雏形 shell.c之print_prompt函数
#define MAX_ARG_NR 16	   // 加上命令名外,最多支持15个参数
#define cmd_len 128    			//最大支持键入128个字符的命令行输入


static char cmd_line[cmd_len] = {0};
//char final_path[MAX_PATH_LEN] = {0};      // 用于洗路径时的缓冲


char cwd_cache[64] = {0};


void print_prompt(void) {
   printf("[rabbit@localhost %s]$ ", cwd_cache);
}

输出命令提示符

shell.c之readline函数

static void readline(char* buf, int32_t count) {
   assert(buf != NULL && count > 0);
   char* pos = buf;

   while (read(stdin_no, pos, 1) != -1 && (pos - buf) < count) { // 在不出错情况下,直到找到回车符才返回
      switch (*pos) {
       
	 case 'n':
	 case 'r':
	    *pos = 0;	   // 添加cmd_line的终止字符0
	    putchar('n');
	    return;

	 case 'b':
	    if (buf[0] != 'b') {		// 阻止删除非本次输入的信息
	       --pos;	   // 退回到缓冲区cmd_line中上一个字符
	       putchar('b');
	    }
	    break;


	 
	 default:
	    putchar(*pos);
	    pos++;
      }
   }
   printf("readline: can`t find enter_key in the cmd_line, max num of char is 128n");
}

从键盘缓冲区一个字符一个字符读入buf,每读一个字符,就处理一个字符于显示屏。
当该字符非控制键时,则将kbd_buf的该字符读入buf即pos,同时pos++指向buf下一个应该读入的位置,表示该字符已读入buf;
当该字符是控制键时,
若是回车或换行,则意味着命令结束,将pos所指置为0即表字符串结尾。显示屏换行再直接return;
若是退格,如果buf[0] == ‘b’即命令行第一个字符就是退格,则跳过此字符不执行退格,因为会退掉命令行提示符;如果buf[0] != ‘b’,意味着buf中一定有字符可以退格,那么执行退格,在显示屏上清除那个字符,并且pos要减一,指向退格前一个字符,此字符已被退格清除,pos指向它,这样下次读入的时候,就可以覆盖此字符。

shell.c之my_shell函数
void my_shell(void) {
   cwd_cache[0] = '/';
   while (1) {
      print_prompt(); 
//      memset(final_path, 0, MAX_PATH_LEN);
      memset(cmd_line, 0, cmd_len);
      readline(cmd_line, cmd_len);
      if (cmd_line[0] == 0) {	 // 若只键入了一个回车
	 continue;
      }
  }
   panic("my_shell: should not be here");
}

死循环不断打印命令行提示符并调用readline来处理键盘缓冲区字符于显示屏。

main.c之main与init函数
int main(void) {
   put_str("I am kerneln");
   init_all();
	cls_screen();
	console_put_str("[rabbit@localhost /]$ ");
	while(1);
	return 0;
}


void init(void) {
   uint32_t ret_pid = fork();
   if(ret_pid) {  // 父进程
       while(1);
   } else {	  // 子进程
      my_shell();
   }
   panic("init: should not be here");
}

主线程执行main函数清屏并且打印了命令行提示符,然后进入死循环
init进程执行init函数,调用fork创建子进程,然后init进程进入if死循环,init的子进程进入my_shell(),
三个线程/进程相互切换,只有init的子进程是像shell一样在反复读键盘命令。其他两个都是死循环浪费cpu。
my_shell()循环打印命令行提示符并调用readline处理键盘缓冲区字符。readline调用read系统调用,执行sys_read,由于参数是标准输入,sys_read会调用ioq_getchar(&kbd_buf)读键盘缓冲区,如果键盘缓冲区没有数据,那么进程会被阻塞。当敲击键盘,键盘中断处理程序会调用ioq_putchar(&kbd_buf, cur_char);向缓冲区输入一个字符,同时唤醒阻塞的进程,sys_read立刻读此字符处理于屏幕上。

实验结果以及main的一个问题


不知道有没有人会对这节书上的代码抱有疑问,因为书上没有解释。

我们知道main函数里面执行了

cls_screen();
	console_put_str("[rabbit@localhost /]$ ");

第二句代码my_shell里面也有,看上去多余
但如果将上述二句代码都注释掉,你会发现看不到my_shell中print_prompt打印的命令行提示符。
事实是,程序先清屏然后打印了main的命令行提示符,之后进入my_shell,非常奇怪的跳过了print_prompt(),执行了readline,没错,经过我的试验,程序第一次命令行,没有执行print_prompt(),但是一定执行了readline,因为不能退格,走了readline的退格switch、case。
这我无法理解。

2.添加Ctrl+u和Ctrl+l keyboard.c的intr_keyboard_handler增加
   
      if (cur_char) {

     
	 if ((ctrl_down_last && cur_char == 'l') || (ctrl_down_last && cur_char == 'u')) {
	    cur_char -= 'a';
	 }
      
      
   
	 if (!ioq_full(&kbd_buf)) {
	    ioq_putchar(&kbd_buf, cur_char);
	 }
	 return;
      }

把Ctrl+u和Ctrl+l减’a’。

shell.c之readline增加
	 
	 case 'l' - 'a': 
	    
	    *pos = 0;
	    
	    clear();
	    
	    print_prompt();
	    
	    printf("%s", buf);
	    break;

	 
	 case 'u' - 'a':
	    while (buf != pos) {
	       putchar('b');
	       *(pos--) = 0;
	    }
	    break;
3.解析键入的字符 shell.c之cmd_parse

static int32_t cmd_parse(char* cmd_str, char** argv, char token) {
   assert(cmd_str != NULL);
   int32_t arg_idx = 0;
   while(arg_idx < MAX_ARG_NR) {
      argv[arg_idx] = NULL;
      arg_idx++;
   }
   char* next = cmd_str;
   int32_t argc = 0;
   
   while(*next) {
      
      while(*next == token) {
	 next++;
      }
      
      if (*next == 0) {
	 break; 
      }
      argv[argc] = next;

     
      while (*next && *next != token) {	  // 在字符串结束前找单词分隔符
	 next++;
      }

      
      if (*next) {
	 *next++ = 0;	// 将token字符替换为字符串结束符0,做为一个单词的结束,并将字符指针next指向下一个字符
      }
   
      
      if (argc > MAX_ARG_NR) {
	 return -1;
      }
      argc++;
   }
   return argc;
}

函数接受命令字符串cmd_str,以及分隔符token()就是’ ‘即空格字符。功能是将cmd_str的命令字依次放入argv数组中。
ASCLL中空格字符值是32,c语言中字符串结尾标志’’值是0.
argv是char*数组,也就是字符串数组。

模拟函数流程
假设命令字符串是“ is dir2 ”
那么这个函数会把“is”,“dir2”放入argv数组。
首先,一个while先把is前面的分隔符空格去除,然后将is放入argv[0],如果is后面是个分隔符空格,那么就把‘’放入is后面作为argv[0]字符串的结束符。接着遍历dir2。
同样的逻辑,清除dir2前面的空格,将dir2放入argv[1],遇到cmd_str的‘’,循环会结束,返回argv。
如果dir2后面有2个及以上的空格,那么会把dir2后的空格替换成’’,同时开始第三次大循环遍历不断跳过空格,当next指向’’后,则需要if来break退出循环。

shell.c之my_shell修改
char* argv[MAX_ARG_NR] = {NULL};
int32_t argc = -1;


void my_shell(void) {
   cwd_cache[0] = '/';
   while (1) {
      print_prompt(); 
printf("what the fuck?");
//      memset(final_path, 0, MAX_PATH_LEN);
      memset(cmd_line, 0, cmd_len);
      readline(cmd_line, cmd_len);

      if (cmd_line[0] == 0) {	 // 若只键入了一个回车
	 continue;
      }

	 argc = -1;
	 argc = cmd_parse(cmd_line, argv, ' ');
	 if (argc == -1) {
	    printf("num of arguments exceed %dn", MAX_ARG_NR);
	    continue;
	 }

      int32_t arg_idx=0;
      	while(arg_idx < argc){
		printf("%s ", argv[arg_idx]);
		arg_idx++;
	}
	printf("n");
  }
   panic("my_shell: should not be here");
}

在readline后,命令字符串已经保存在buf了,对其调用cmd_parse,得到的argv依次打印字符串。

实验效果

4.添加系统调用 thread.c之sys_ps内核实现

static void pad_print(char* buf, int32_t buf_len, void* ptr, char format) {
   memset(buf, 0, buf_len);
   uint8_t out_pad_0idx = 0;
   switch(format) {
      case 's':
	 out_pad_0idx = sprintf(buf, "%s", ptr);
	 break;
      case 'd':
	 out_pad_0idx = sprintf(buf, "%d", *((int16_t*)ptr));
      case 'x':
	 out_pad_0idx = sprintf(buf, "%x", *((uint32_t*)ptr));
   }
   while(out_pad_0idx < buf_len) { // 以空格填充
      buf[out_pad_0idx] = ' ';
      out_pad_0idx++;
   }
   sys_write(stdout_no, buf, buf_len - 1);
}


static bool elem2thread_info(struct list_elem* pelem, int arg UNUSED) {
   struct task_struct* pthread = elem2entry(struct task_struct, all_list_tag, pelem);
   char out_pad[16] = {0};

   pad_print(out_pad, 16, &pthread->pid, 'd');

   if (pthread->parent_pid == -1) {
      pad_print(out_pad, 16, "NULL", 's');
   } else { 
      pad_print(out_pad, 16, &pthread->parent_pid, 'd');
   }

   switch (pthread->status) {
      case 0:
	 pad_print(out_pad, 16, "RUNNING", 's');
	 break;
      case 1:
	 pad_print(out_pad, 16, "READY", 's');
	 break;
      case 2:
	 pad_print(out_pad, 16, "BLOCKED", 's');
	 break;
      case 3:
	 pad_print(out_pad, 16, "WAITING", 's');
	 break;
      case 4:
	 pad_print(out_pad, 16, "HANGING", 's');
	 break;
      case 5:
	 pad_print(out_pad, 16, "DIED", 's');
   }
   pad_print(out_pad, 16, &pthread->elapsed_ticks, 'x');

   memset(out_pad, 0, 16);
   ASSERT(strlen(pthread->name) < 17);
   memcpy(out_pad, pthread->name, strlen(pthread->name));
   strcat(out_pad, "n");
   sys_write(stdout_no, out_pad, strlen(out_pad));
   return false;	// 此处返回false是为了迎合主调函数list_traversal,只有回调函数返回false时才会继续调用此函数
}

 
void sys_ps(void) {
   char* ps_title = "PID            PPID           STAT           TICKS          COMMANDn";
   sys_write(stdout_no, ps_title, strlen(ps_title));
   list_traversal(&thread_all_list, elem2thread_info, 0);
}

ps用来打印进程的pid、ppid、状态、运行时间片和进程名。
sys_ps调用list_traversal,遍历所有存在的进程,获得该pcb,执行elem2thread_info;
而elem2thread_info则是不断调用pad_print来打印进程信息。
pad_print:以固定长度打印字符串,不够后面用空格填充。

补全所有内核已实现的系统调用 syscall.h
enum SYSCALL_NR {
   SYS_GETPID,
   SYS_WRITE,
   SYS_MALLOC,
   SYS_FREE,
   SYS_FORK,
   SYS_READ,
   SYS_PUTCHAR,
   SYS_CLEAR,
   SYS_GETCWD,
   SYS_OPEN,
   SYS_CLOSE,
   SYS_LSEEK,
   SYS_UNlink,
   SYS_MKDIR,
   SYS_OPENDIR,
   SYS_CLOSEDIR,
   SYS_CHDIR,
   SYS_RMDIR,
   SYS_READDIR,
   SYS_REWINDDIR,
   SYS_STAT,
   SYS_PS

};
syscall.c

char* getcwd(char* buf, uint32_t size) {
   return (char*)_syscall2(SYS_GETCWD, buf, size);
}


int32_t open(char* pathname, uint8_t flag) {
   return _syscall2(SYS_OPEN, pathname, flag);
}


int32_t close(int32_t fd) {
   return _syscall1(SYS_CLOSE, fd);
}


int32_t lseek(int32_t fd, int32_t offset, uint8_t whence) {
   return _syscall3(SYS_LSEEK, fd, offset, whence);
}


int32_t unlink(const char* pathname) {
   return _syscall1(SYS_UNlink, pathname);
}


int32_t mkdir(const char* pathname) {
   return _syscall1(SYS_MKDIR, pathname);
}


struct dir* opendir(const char* name) {
   return (struct dir*)_syscall1(SYS_OPENDIR, name);
}


int32_t closedir(struct dir* dir) {
   return _syscall1(SYS_CLOSEDIR, dir);
}


int32_t rmdir(const char* pathname) {
   return _syscall1(SYS_RMDIR, pathname);
}


struct dir_entry* readdir(struct dir* dir) {
   return (struct dir_entry*)_syscall1(SYS_READDIR, dir);
}


void rewinddir(struct dir* dir) {
   _syscall1(SYS_REWINDDIR, dir);
}


int32_t stat(const char* path, struct stat* buf) {
   return _syscall2(SYS_STAT, path, buf);
}


int32_t chdir(const char* path) {
   return _syscall1(SYS_CHDIR, path);
}


void ps(void) {
   _syscall0(SYS_PS);
}

syscall_init.c

void syscall_init(void) {
   put_str("syscall_init startn");
   syscall_table[SYS_GETPID]	    = sys_getpid;
   syscall_table[SYS_WRITE]	    = sys_write;
   syscall_table[SYS_MALLOC]	    = sys_malloc;
   syscall_table[SYS_FREE]	    = sys_free;
   syscall_table[SYS_FORK]	    = sys_fork;
   syscall_table[SYS_READ]	    = sys_read;
   syscall_table[SYS_PUTCHAR]	    = console_put_char;
   syscall_table[SYS_CLEAR]	    = cls_screen;
   syscall_table[SYS_GETCWD]	    = sys_getcwd;
   syscall_table[SYS_OPEN]	    = sys_open;
   syscall_table[SYS_CLOSE]	    = sys_close;
   syscall_table[SYS_LSEEK]	    = sys_lseek;
   syscall_table[SYS_UNlink]	    = sys_unlink;
   syscall_table[SYS_MKDIR]	    = sys_mkdir;
   syscall_table[SYS_OPENDIR]	    = sys_opendir;
   syscall_table[SYS_CLOSEDIR]	    = sys_closedir;
   syscall_table[SYS_CHDIR]	    = sys_chdir;
   syscall_table[SYS_RMDIR]	    = sys_rmdir;
   syscall_table[SYS_READDIR]	    = sys_readdir;
   syscall_table[SYS_REWINDDIR]	    = sys_rewinddir;
   syscall_table[SYS_STAT]	    = sys_stat;
   syscall_table[SYS_PS]	    = sys_ps;

   put_str("syscall_init donen");
}
5.路径解析转换 buildin_cmd.c

static void wash_path(char* old_abs_path, char* new_abs_path) {
   assert(old_abs_path[0] == '/');
   char name[MAX_FILE_NAME_LEN] = {0};    
   char* sub_path = old_abs_path;
   sub_path = path_parse(sub_path, name);
   if (name[0] == 0) { // 若只键入了"/",直接将"/"存入new_abs_path后返回 
      new_abs_path[0] = '/';
      new_abs_path[1] = 0;
      return;
   }
   new_abs_path[0] = 0;	   // 避免传给new_abs_path的缓冲区不干净
   strcat(new_abs_path, "/");
   while (name[0]) {
      
      if (!strcmp("..", name)) {
	 char* slash_ptr =  strrchr(new_abs_path, '/');
       
	 if (slash_ptr != new_abs_path) {	// 如new_abs_path为“/a/b”,".."之后则变为“/a”
	    *slash_ptr = 0;
	 } else {	      // 如new_abs_path为"/a",".."之后则变为"/"
      
	    *(slash_ptr + 1) = 0;
	 }
      } else if (strcmp(".", name)) {	  // 如果路径不是‘.’,就将name拼接到new_abs_path
	 if (strcmp(new_abs_path, "/")) {	  // 如果new_abs_path不是"/",就拼接一个"/",此处的判断是为了避免路径开头变成这样"//"
	    strcat(new_abs_path, "/");
	 }
	 strcat(new_abs_path, name);
      }  // 若name为当前目录".",无须处理new_abs_path

      
      memset(name, 0, MAX_FILE_NAME_LEN);
      if (sub_path) {
	 sub_path = path_parse(sub_path, name);
      }
   }
}


void make_clear_abs_path(char* path, char* final_path) {
   char abs_path[MAX_PATH_LEN] = {0};
   
   if (path[0] != '/') {      // 若输入的不是绝对路径,就拼接成绝对路径
      memset(abs_path, 0, MAX_PATH_LEN);
      if (getcwd(abs_path, MAX_PATH_LEN) != NULL) {
	 if (!((abs_path[0] == '/') && (abs_path[1] == 0))) {	     // 若abs_path表示的当前目录不是根目录/
	    strcat(abs_path, "/");
	 }
      }
   }
   strcat(abs_path, path);
   wash_path(abs_path, final_path);
}

wash_path函数:将旧绝对路径old_abs_path转化为绝对路径new_abs_path(不含"。"、“。。”)

假设old_abs_path=/a/b/…/c
1.首先path_parse获得a存入sub_path,同时new_abs_path[0] = ‘/’,进入while循环。
2.由于a不是“。”也不是“。。”,直接加入new_abs_path,再path_parse获得b。
3.b也不是“。”和“。。”,但由于此时new_abs_path为/a,所以加入了/b于new_abs_path。再path_parse获得“。。”
4.“。。”的作用就是要清除当前目录,所以strrchr将slash_ptr指向到/a/b中ab之间的/,将其修改为即可。再path_parse获得c。
5.由于new_abs_path目前为/a0b,strcmp、strcat都会认为这是/a,所以加入/c,new_abs_path变成/a/c。

假设old_abs_path=/…
那么strrchr将slash_ptr指向的/等于new_abs_path即首地址,意味着这个/是根目录/,所以*(slash_ptr+1) = 0,new_abs_path变成了/0即/

如果old_abs_path中有“。”表示当前目录,new_abs_path什么也不做

make_clear_abs_path:接受path,调用getcwd获取当前工作目录,将path加入构成可含"…"、“。”的绝对路径。
调用wash_path,再转化成不含“。”、“。。”的绝对路径。

shell.c用于测试

void my_shell(void) {
   cwd_cache[0] = '/';
   while (1) {
      print_prompt(); 
printf("what the fuck?");
//      memset(final_path, 0, MAX_PATH_LEN);
      memset(cmd_line, 0, cmd_len);
      readline(cmd_line, cmd_len);

      if (cmd_line[0] == 0) {	 // 若只键入了一个回车
	 continue;
      }

	 argc = -1;
	 argc = cmd_parse(cmd_line, argv, ' ');
	 if (argc == -1) {
	    printf("num of arguments exceed %dn", MAX_ARG_NR);
	    continue;
	 }
	char buf[MAX_PATH_LEN] = {0};
      int32_t arg_idx=0;
      	while(arg_idx < argc){
      	make_clear_abs_path(argv[arg_idx], buf);
		printf("%s -> %s", argv[arg_idx], buf);
		arg_idx++;
	}
	printf("n");
  }
   panic("my_shell: should not be here");
}

只修改了这两句。
make_clear_abs_path(argv[arg_idx], buf);
printf("%s -> %s", argv[arg_idx], buf);

实验效果

6.实现ls、cd、mkdir、ps、rm等命令 buildin_cmd.c之shell 内部命令的内建函数

void buildin_pwd(uint32_t argc, char** argv UNUSED) {
   if (argc != 1) {
      printf("pwd: no argument support!n");
      return;
   } else {
      if (NULL != getcwd(final_path, MAX_PATH_LEN)) {
	 printf("%sn", final_path); 
      } else {
	 printf("pwd: get current work directory failed.n");
      }
   }
}


char* buildin_cd(uint32_t argc, char** argv) {
   if (argc > 2) {
      printf("cd: only support 1 argument!n");
      return NULL;
   }

   
   if (argc == 1) {
      final_path[0] = '/';
      final_path[1] = 0;
   } else {
      make_clear_abs_path(argv[1], final_path);
   }

   if (chdir(final_path) == -1) {
      printf("cd: no such directory %sn", final_path);
      return NULL;
   }
   return final_path;
}


void buildin_ls(uint32_t argc, char** argv) {
   char* pathname = NULL;
   struct stat file_stat;
   memset(&file_stat, 0, sizeof(struct stat));
   bool long_info = false;
   uint32_t arg_path_nr = 0;
   uint32_t arg_idx = 1;   // 跨过argv[0],argv[0]是字符串“ls”
   while (arg_idx < argc) {
      if (argv[arg_idx][0] == '-') {	  // 如果是选项,单词的首字符是-
	 if (!strcmp("-l", argv[arg_idx])) {         // 如果是参数-l
	    long_info = true;
	 } else if (!strcmp("-h", argv[arg_idx])) {   // 参数-h
	    printf("usage: -l list all infomation about the file.n-h for helpnlist all files in the current dirctory if no optionn"); 
	    return;
	 } else {	// 只支持-h -l两个选项
	    printf("ls: invalid option %snTry `ls -h' for more information.n", argv[arg_idx]);
	    return;
	 }
      } else {	     // ls的路径参数
	 if (arg_path_nr == 0) {
	    pathname = argv[arg_idx];
	    arg_path_nr = 1;
	 } else {
	    printf("ls: only support one pathn");
	    return;
	 }
      }
      arg_idx++;
   } 
   
   if (pathname == NULL) {	 // 若只输入了ls 或 ls -l,没有输入操作路径,默认以当前路径的绝对路径为参数.
      if (NULL != getcwd(final_path, MAX_PATH_LEN)) {
	 pathname = final_path;
      } else {
	 printf("ls: getcwd for default path failedn");
	 return;
      }
   } else {
      make_clear_abs_path(pathname, final_path);
      pathname = final_path;
   }

   if (stat(pathname, &file_stat) == -1) {
      printf("ls: cannot access %s: No such file or directoryn", pathname);
      return;
   }
   if (file_stat.st_filetype == FT_DIRECTORY) {
      struct dir* dir = opendir(pathname);
      struct dir_entry* dir_e = NULL;
      char sub_pathname[MAX_PATH_LEN] = {0};
      uint32_t pathname_len = strlen(pathname);
      uint32_t last_char_idx = pathname_len - 1;
      memcpy(sub_pathname, pathname, pathname_len);
      if (sub_pathname[last_char_idx] != '/') {
	 sub_pathname[pathname_len] = '/';
	 pathname_len++;
      }
      rewinddir(dir);
      if (long_info) {
	 char ftype;
	 printf("total: %dn", file_stat.st_size);
	 while((dir_e = readdir(dir))) {
	    ftype = 'd';
	    if (dir_e->f_type == FT_REGULAR) {
	       ftype = '-';
	    } 
	    sub_pathname[pathname_len] = 0;
	    strcat(sub_pathname, dir_e->filename);
	    memset(&file_stat, 0, sizeof(struct stat));
	    if (stat(sub_pathname, &file_stat) == -1) {
	       printf("ls: cannot access %s: No such file or directoryn", dir_e->filename);
	       return;
	    }
	    printf("%c  %d  %d  %sn", ftype, dir_e->i_no, file_stat.st_size, dir_e->filename);
	 }
      } else {
	 while((dir_e = readdir(dir))) {
	    printf("%s ", dir_e->filename);
	 }
	 printf("n");
      }
      closedir(dir);
   } else {
      if (long_info) {
	 printf("-  %d  %d  %sn", file_stat.st_ino, file_stat.st_size, pathname);
      } else {
	 printf("%sn", pathname);  
      }
   }
}


void buildin_ps(uint32_t argc, char** argv UNUSED) {
   if (argc != 1) {
      printf("ps: no argument support!n");
      return;
   }
   ps();
}


void buildin_clear(uint32_t argc, char** argv UNUSED) {
   if (argc != 1) {
      printf("clear: no argument support!n");
      return;
   }
   clear();
}


int32_t buildin_mkdir(uint32_t argc, char** argv) {
   int32_t ret = -1;
   if (argc != 2) {
      printf("mkdir: only support 1 argument!n");
   } else {
      make_clear_abs_path(argv[1], final_path);
      
      if (strcmp("/", final_path)) {
	 if (mkdir(final_path) == 0) {
	    ret = 0;
	 } else {
	    printf("mkdir: create directory %s failed.n", argv[1]);
	 }
      }
   }
   return ret;
}


int32_t buildin_rmdir(uint32_t argc, char** argv) {
   int32_t ret = -1;
   if (argc != 2) {
      printf("rmdir: only support 1 argument!n");
   } else {
      make_clear_abs_path(argv[1], final_path);
   
      if (strcmp("/", final_path)) {
	 if (rmdir(final_path) == 0) {
	    ret = 0;
	 } else {
	    printf("rmdir: remove %s failed.n", argv[1]);
	 }
      }
   }
   return ret;
}


int32_t buildin_rm(uint32_t argc, char** argv) {
   int32_t ret = -1;
   if (argc != 2) {
      printf("rm: only support 1 argument!n");
   } else {
      make_clear_abs_path(argv[1], final_path);
   
      if (strcmp("/", final_path)) {
	 if (unlink(final_path) == 0) {
	    ret = 0;
	 } else {
	    printf("rm: delete %s failed.n", argv[1]);
	 }
	    
      }
   }
   return ret;
}

buildin_pwd:除命令无参数,则调用系统调用getcwd打印当前工作目录。

buildin_cd:
若只键入cd而无参数,直接返回到根目录,
cd有参数,调用make_clear_abs_path将进程当前工作目录加上参数转化成绝对路径返回。

buildin_ls:

buildin_ps:除命令无额外参数,则系统调用ps,打印进程的pid、ppid、状态、运行时间片和进程名。、

buildin_clear:除命令无额外参数,则调用系统调用clear清屏。

buildin_mkdir:除命令必须要一个目录路径名参数,调用make_clear_abs_path得到参数对应的绝对路径,只要这个绝对路径不是根目录,则调用mkdir系统调用创建此目录。

buildin_rmdir:同上。

buildin_rm:同上,调用系统调用unlink删除文件和目录。

shell.c之my_shell增加实现解析命令调用内建函数

void my_shell(void) {
   cwd_cache[0] = '/';
   while (1) {
      print_prompt(); 
printf("what the fuck?");
     memset(final_path, 0, MAX_PATH_LEN);
      memset(cmd_line, 0, cmd_len);
      readline(cmd_line, cmd_len);

      if (cmd_line[0] == 0) {	 // 若只键入了一个回车
	 continue;
      }

	 argc = -1;
	 argc = cmd_parse(cmd_line, argv, ' ');
	 if (argc == -1) {
	    printf("num of arguments exceed %dn", MAX_ARG_NR);
	    continue;
	 }

	 if (!strcmp("ls", argv[0])) {
      buildin_ls(argc, argv);
   } else if (!strcmp("cd", argv[0])) {
      if (buildin_cd(argc, argv) != NULL) {
	 memset(cwd_cache, 0, MAX_PATH_LEN);
	 strcpy(cwd_cache, final_path);
      }
   } else if (!strcmp("pwd", argv[0])) {
      buildin_pwd(argc, argv);
   } else if (!strcmp("ps", argv[0])) {
      buildin_ps(argc, argv);
   } else if (!strcmp("clear", argv[0])) {
      buildin_clear(argc, argv);
   } else if (!strcmp("mkdir", argv[0])){
      buildin_mkdir(argc, argv);
   } else if (!strcmp("rmdir", argv[0])){
      buildin_rmdir(argc, argv);
   } else if (!strcmp("rm", argv[0])) {
      buildin_rm(argc, argv);
   } 
  }
   panic("my_shell: should not be here");
}

my_shell调用cmd_parse后,已经获得参数数组argv,命令就是argv[0],命令后面的参数就是argv[1]等等,argc就是参数个数。

实验效果

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/729102.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号