【Linux】编写一个浅易的shell

老婆出轨  金牌会员 | 2024-6-26 16:42:21 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 528|帖子 528|积分 1584

头脑导图


学习目标

       将浅易的shell代码进行编写。
一、叙述shell的基本思绪

       在历程步伐替换中,我们可以将一个指令交给子历程,让子历程去完成这个指令。假如这个命令是一个内建命令,我们需要将这个命令交给bash进行处理。
       大致思绪是:首先,我们先打印出来一行命令行,代表我们的主机名,名字和当前路径;之后捕捉一行指令命令,将指令命令进行分割,存储在字符串指针数组中;然后,将这个字符串指针数组交给exec*函数进行步伐替换。
二、输出一个命令行

2.1 思绪和代码

           

       我们可以观察xshell中的这个命令行中的内容,我们可以仿效这个命令行中的内容打印出自己的xshell的命令行。现在我们应该思索从那里获取这个内容呢??在环境变量中,我们可以发现有这些内容:

       我们可以利用getenv函数来分别获取USER、HOSTNAME、PWD的内容,之后利用snprintf函数将这个内容串联起来打印到一个字符串数组,以便将这个命名行打印出来。
  1. const char* Getname()
  2. {
  3.   const char* name = getenv("USER");
  4.   if(name == NULL) return "None";
  5.   return name;
  6. }
  7. const char* Gethostname()
  8. {
  9.   const char* hostname = getenv("HOSTNAME");
  10.   if(hostname == NULL) return "None";
  11.   return hostname;
  12. }
  13. const char* Getpwd()
  14. {
  15.   const char* pwd = getenv("PWD");
  16.   if(pwd == NULL) return "None";
  17.   return pwd;
  18. }
  19. void MakeCommendLine(char commend[], size_t size)
  20. {
  21.   const char* name = Getname();
  22.   const char* hostname = Gethostname();
  23.   const char* pwd = Getpwd();
  24.   SkipPath(pwd);
  25.   snprintf(commend, size, "[%s@%s %s]>" , name, hostname, strlen(pwd) == 1 ? "/" : pwd + 1);
  26.   printf("%s", commend);
  27.   fflush(stdout);
  28. }
复制代码
2.2 简要介绍一下snprintf函数

  1. char *getenv(const char *name)
复制代码
       函数的用途:该函数返回一个以 null 结尾的字符串,该字符串为被请求环境变量的值。假如该环境变量不存在,则返回 NULL。
2.3 简要介绍一下getenv函数

  1. int snprintf(char *str, size_t size, const char *format, ...)
复制代码

  • 假如格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符('\0');
  • 假如格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符('\0'),返回值为欲写入的字符串长度。
  • snprintf的返回值n,当调用失败时,n为负数,当调用成功时,n为格式化的字符串的总长度(不包括\0),当然这个字符串有可能被截断,由于buf的长度不够放下整个字符串。
三、获取用户命令字符串

       我们在输入指令命令时,会有空格,我们不能利用scanf函数,所以我们应该利用fgets函数,将指令命令进行接收。
  1. int getecho(char* commend, size_t n)
  2. {
  3.   char* s = fgets(commend, n, stdin);
  4.   if(s == NULL) return -1;
  5.   commend[strlen(commend) - 1] = '\0';
  6.   return strlen(commend);
  7. }
复制代码
简要介绍一下fgets函数

  1. char *fgets(char *restrict str, int size, FILE *restrict stream)
复制代码
函数的用途:fgets函数就是用来读取一行数据的,从第三个参数指定的流中读取最多第二个参数大小的字符到第一个参数指定的容器地址中。
函数的返回值:在正常环境下fgets()函数的返回值和它第一个参数相同。即读取到数据后存储的容器地址。但是假如读取堕落或读取文件时文件为空,则返回一个空指针。
函数的注意事项:在fgets()函数的眼里,换行符’\n’也是它要读取的一个平凡字符而已。在读取键盘输入的时候会把末了输入的回车符也存进数组里面,即会把’\n’也存进数组里面,而又由于字符串自己会是以’\0’结尾的。所以在输入字符个数没有超过第二个参数指定大小之前,你输入n个字符按下回车输入,fgets()存储进第一个参数指定内存地址的是n+2个字节。末了面会多出一个’\n’和一个’\0’,而且’\n’是在’\0’的前面一个(\n\0)。其余部分请看大佬写的:fgets函数详解
四、切割命令字符串

       在获取到输入的指令字符串后,我们需要将指令进行切割。由于指令间隔是空格,我们可以利用strtok函数进行分割指令。
  1. char* gargv[SIZE];
  2. void slashecho(char commend[], size_t n)
  3. {
  4.   gargv[0] = strtok(commend, SEP);
  5.   int cnt = 1;
  6.   while ((gargv[cnt++] = strtok(NULL, SEP))); // 故意写出赋值,
  7. }
复制代码
简要介绍一下strtok函数

  1. char *strtok(char s[], const char *delim);
复制代码
函数的用途:分解字符串为一组字符串。s为要分解的字符,delim为分隔符字符(假如传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。
函数的返回值:从s开头开始的一个个被分割的串。当s中的字符查找到末端时,返回NULL。假如查找不到delim中的字符时,返回当前strtok的字符串的指针。所有delim中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。 
五、创建子历程进行历程替换

5.1 查抄指令是否为内建命令

       比较马虎,直接利用if语句进行逐一的判断,假如成功,则是内建命令;假如失败,则是平凡命令。假如是内建命令,我们可以重新创建一个函数来单独的进行内建命令的实行。
  1. int ChickBuliding()
  2. {
  3.   int yes = 0;
  4.   const char* entercommend = gargv[0];
  5.   if(strcmp(entercommend, "cd") == 0)
  6.   {
  7.     yes = 1;
  8.     Cd();
  9.   }
  10.   else if(strcmp(entercommend, "echo") == 0 && strcmp(gargv[1], "$?") == 0)
  11.   {
  12.     yes = 1;
  13.     printf("%d\n", lastcode);
  14.     lastcode = 0;
  15.   }
  16.   return yes;
  17. }
复制代码
       比如,说cd命令,我们可以利用chdir函数改变当前工作目次,getcwd函数将当前工作目次的绝对路径复制到参数buffer所指的内存空间中,参数size为buf的空间大小。在将获取到的路径写入cwd中,末了利用putenv函数将cwd写入环境变量中。
  1. void Cd()
  2. {
  3.   const char* path = gargv[1];
  4.   if(path == NULL) path = Home();
  5.   chdir(path);
  6.   // 刷新环境变量
  7.   char temp[SIZE * 2];
  8.   // 获取当前路径
  9.   getcwd(temp, sizeof temp);
  10.   // 将当前路径写入cwd中
  11.   snprintf(cwd, sizeof cwd, "PWD=%s", temp);
  12.   // 将cwd写入环境变量中
  13.   putenv(cwd);
  14. }
复制代码
       还有一个echo $?命令,直接判断是否为这个命令,假如是这个命令,直接将lastcode返回,并将lastcode重新置为0。
5.2 指令是平凡命令

       我们可以创建一个子历程,利用exec*函数进行步伐历程替换。末了,让父历程进行等待,假如父历程等待成功,则查抄退出码是否为0,假如不为0,将错误信息打印出来。
  1. void executecommend()
  2. {
  3.   pid_t id = fork();
  4.   if(id < 0)
  5.   {
  6.     Die();
  7.   }
  8.   else if(id == 0)
  9.   {
  10.     execvp(gargv[0], gargv);
  11.     exit(1);
  12.   }
  13.   else
  14.   {
  15.     int status = 0;
  16.     pid_t rid = waitpid(id, &status, 0);
  17.     if(rid > 0)
  18.     {
  19.       lastcode = WEXITSTATUS(status);
  20.       if(lastcode != 0) printf("%s:%s:%d\n", gargv[0], strerror(lastcode), lastcode);
  21.     }
  22.   }
  23. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

老婆出轨

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表