当前位置: 首页 > news >正文

wordpress 数卡seo优化博客

wordpress 数卡,seo优化博客,深圳博大建设公司,政协网站法治建设Linux 自主 shell 编写(C 语言实现) 效果主要步骤打印命令行提示符获取用户命令字符串切割用户命令字符串执行命令循环 至此源码(简易半成品)细节内建命令问题cd 退出码问题echo 查看退出码 完整源码makefilemyshell.c 效果 效果…

Linux 自主 shell 编写(C 语言实现)

  • 效果
  • 主要步骤
    • 打印命令行提示符
    • 获取用户命令字符串
    • 切割用户命令字符串
    • 执行命令
    • 循环
  • 至此源码(简易半成品)
  • 细节
    • 内建命令问题
      • cd
    • 退出码问题
    • echo 查看退出码
  • 完整源码
    • makefile
    • myshell.c

效果

效果嘛和 命令行解释器 一模一样,这里就不贴图了

只是把 # (超管)$ (普通用户) 符号改为 > 以作区分

注意哦: 删除键不能直接使用,要配合 ctrl 键才行

主要步骤

打印命令行提示符

在 Linux 终端(命令行)里,首先看到的是 命令行提示符

[exercise@localhost my_shell]$ 

shell 一旦跑起来,定是要先打印 命令行提示符 的,但是这玩意对于不同的用户是不一样的呀,所以不能单纯的打印出来,而是要获取用户名,主机名等等,如何获取?目前来说对各种 系统接口还不熟,那就直接使用 环境变量

命令行执行 env 命令,就可以看到很多 环境变量 ^ ^

系统环境变量 很多,不容易直接得到想要的,所以可以使用库函数 getenv 来获取,需要包含头文件 #include <stdlib.h> ,函数原型如下:

char *getenv(const char *name);

那么 用户名主机名工作目录 分别在 USERHOSTNAMEPWD 内,直接使用 getenv 函数获取即可

最后使用 snprintf() 函数拼接成 命令行提示符 的格式即可,函数原型:

int snprintf(char *str, size_t size, const char *format, ...);

获取用户命令字符串

C 语言 获取键盘字符串 可以使用库函数 scanf() ,但它遇到空格可就不继续读取了,而它的高端玩法还不熟

咱就老老实实使用 fgets 函数,原型:

char *fgets(char *s, int size, FILE *stream);

切割用户命令字符串

这一步是必要的,因为日后一定是需要 进程替换 的,进程替换 就一定需要将用户命令以空格为分隔符打散分开,是库函数参数的原因,是刚需

如何实现呢?倒是也很简单,我们可以直接将空格替换为 '\0' ,那么一个长串就变为若干个子串

如果要执行用户输入的命令,是要创建子进程来完成的;那我们就需要为进程传递 命令行参数 来实现,毕竟不同的选项具有不同的功能,所以切割的字串分别放入 命令行参数表 argv[] 里即可,argv 的每一个元素都是一个指针,指向被切完成的子串(最后一个指针为 NULL

那么只需要将 argv 的第一个元素指向第一个子串,第二个元素指向第二个子串,以此类推

但这比较麻烦,咱可以使用库函数 strtok() 完成; 命令行参数表 也可以设置为全局的,好调用

执行命令

获取用户的命令后,不执行等啥呢?

当然啦,执行命令不是自己当前进程来执行,而是 创建子进程,在利用 进程替换,此时子进程就可以执行你想要的全新的代码

循环

一个 shell 怎么能只运行一条命令呢?所以我们需要将上述过程循环起来,这样就能无限制运行命令

至此,简易到不能再简易的 shell 就实现好了

至此源码(简易半成品)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32char* gArgv[NUM];const char* getUserName()
{const char* username = getenv("USER");if (username == NULL) return "None";return username;
}const char* getHostName()
{const char* hostname = getenv("HOSTNAME");if (hostname == NULL) return "None";return hostname;
}// 临时
const char* getCwd()
{const char* cwd = getenv("PWD");if (cwd == NULL) return "None";return cwd;
}void MakeCommandLineAndPrint()
{char line[SIZE];const char* username = getUserName();const char* hostname = getHostName();const char* cwd = getCwd();snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, cwd);printf("%s", line);fflush(stdout);
}int GetUserCommand(char command[], size_t size)
{char* s = fgets(command, size, stdin);if (s == NULL) return -1;command[strlen(command) - 1] = ZERO;return strlen(command);
}void SplitCommand(char command[], int size)
{gArgv[0] = strtok(command, SEP);int index = 1;while ((gArgv[index++] = strtok(NULL, SEP)));}void Die()
{exit(1);
}void ExecuteCommmand()
{pid_t id = fork();if (id < 0) Die();else if (id == 0){// childexecvp(gArgv[0], gArgv);exit(errno);}else {// parentint status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){}}
}int main()
{int quit = 0;while (!quit){// 自己需要输出一个命令行MakeCommandLineAndPrint();// 获取用户命令字符串char usercommand[SIZE];int num = GetUserCommand(usercommand, sizeof(usercommand));if (num < 0) return 1;// 分割用户命令字符串SplitCommand(usercommand, sizeof(usercommand));// 执行命令ExecuteCommmand();}return 0;
}

细节

上面的代码虽然说可以运行,但有很多漏洞和细节尚未修补实现,接下来一一填补:

内建命令问题

cd

举个例子吧,上面的代码先跑起来,不说其他的,试试 cd 命令能不能正常运行

不能正常运行!!! 这个的漏洞不是一般的大,并不是不能使用 cd 命令,而是命令 cd 对咱这个 shell 不起任何作用

而如果你们运行上面的残本代码,会发现当前的工作路径是一串绝对路径,要想切割最后一个目录拿过来倒也容易,但 cd 还是无法生效啊

为什么?

其实很简单,我们是实现 shell 的方法是 创建子进程,然后拿想要的进程去替换这个子进程;由于进程的独立性,子进程会影响父进程吗?肯定不会,那子进程执行 cd 命令和你父进程有什么关系呢?子进程执行 cd 命令的时候父进程在干嘛?在那 wait 呢!!!

所以这样实现父进程 shell 的工作路径改不了的,那如何能改?当然是父进程自己执行咯

所以像 cd 这样的命令是 内建命令

既如此,观察上述代码,在执行命令之前 需要检查是否有 内建命令

如何检查?

直接判断不就行了,它有几个 内建命令,咱就判断几次,如果用户输入的是 cd 命令,shell 就自己执行

如何执行?这种涉及系统的东西当然要 系统调用 嘛,chdir 可以将当前进程的工作路径,切换至你想要的路径,那咱们就可以直接 将用户输入的路径 传进 chdir 的参数里即可

注意如果直接运行 cd 命令,是返回用户家目录的;所以如果切割后的子串只有 cd ,第二个元素路径为 NULL 的话,可直接返回 用户家目录(可函数实现)

改完之后记得要修改 shell 下一次打印出来的命令行路径,因为这是被我封装为函数的,直接修改较为麻烦,但我是从环境变量里获取的,所以直接修改环境变量即可:
首先使用函数 getcwd ,此函数可以直接获取真正的工作路径,然后拼接 PWD ,再使用函数 putenv() 来刷新环境变量

// 内建命令 cd 的执行过程
void Cd()
{// 获取 cd 路径const char* path = gArgv[1];if (path == NULL) path = getHome();// 此时 path 一定存在,那么可以直接使用 系统调用 修改工作路径chdir(path);// 获取此时的工作路径char temp[SIZE * 2];getcwd(temp, sizeof(temp));//  拼接 PWD 环境变量snprintf(Cwd, sizeof(Cwd), "PWD=%s", temp);// 刷新环境变量putenv(Cwd);
}// 检查是否有内建命令
int CheckBuildIn()
{int yes = 0;const char* enter_cmd = gArgv[0];if (strcmp(enter_cmd, "cd") == 0){yes = 1;Cd();}// 继续判断其他内建命令...return yes;
}

至此,最大的坑已经被补上了,至于命令行解释器里,当前工作目录的切割,使用宏函数可直接实现(后附完整源码),这里不做解释

// 宏函数
#define SkipPath(pCwd) do { pCwd += (strlen(pCwd) - 1); while (*pCwd != '/') --pCwd; } while (0)

退出码问题

父进程是一定要得到子进程的退出码的,不然有问题无法准确反馈给用户

具体实现也是进程替换的内容,非常简单,看源码

echo 查看退出码

当然是下面这个命令啦:

echo $?

和上面 cd 命令一样,需要在 CheckBuildIn 函数里进行判断是否有 echo $? 命令,逻辑编写十分简单,在 CheckBuildIn 函数里编写即可:

else if (strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0)
{yes = 1;printf("%d\n", lastcode);lastcode = 0;
}

完整源码

CentOS 7.9 平台 gcc 编译测试,进入 可执行文件 MyShell 所在目录下, ./MyShell 即可运行

makefile

bin=MyShell
src=myshell.c$(bin):$(src)gcc $^ -o $@
.PHONY:clean
clean:rm -f $(bin)

myshell.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define SkipPath(pCwd) do { pCwd += (strlen(pCwd) - 1); while (*pCwd != '/') --pCwd; } while (0)char* gArgv[NUM];
char Cwd[SIZE];
int lastcode = 0;const char* getHome()
{const char* home = getenv("HOME");if (home == NULL) return "/";return home;
}const char* getUserName()
{const char* username = getenv("USER");if (username == NULL) return "None";return username;
}const char* getHostName()
{const char* hostname = getenv("HOSTNAME");if (hostname == NULL) return "None";return hostname;
}// 临时
const char* getCwd()
{const char* cwd = getenv("PWD");if (cwd == NULL) return "None";return cwd;
}void MakeCommandLineAndPrint()
{char line[SIZE];const char* username = getUserName();const char* hostname = getHostName();const char* cwd = getCwd();SkipPath(cwd);snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, strlen(cwd) == 1 ? "/" : (cwd + 1));printf("%s", line);fflush(stdout);
}int GetUserCommand(char command[], size_t size)
{char* s = fgets(command, size, stdin);if (s == NULL) return -1;command[strlen(command) - 1] = ZERO;return strlen(command);
}void SplitCommand(char command[], int size)
{(void)size;gArgv[0] = strtok(command, SEP);int index = 1;while ((gArgv[index++] = strtok(NULL, SEP)));}void Die()
{exit(1);
}void ExecuteCommmand()
{pid_t id = fork();if (id < 0) Die();else if (id == 0){// childexecvp(gArgv[0], gArgv);exit(errno);}else {// parentint status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);if (lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);}}
}void Cd()
{// 获取 cd 路径const char* path = gArgv[1];if (path == NULL) path = getHome();// 此时 path 一定存在,那么可以直接使用 系统调用 修改工作路径chdir(path);// 获取此时的工作路径char temp[SIZE * 2];getcwd(temp, sizeof(temp));//  拼接 PWD 环境变量snprintf(Cwd, sizeof(Cwd), "PWD=%s", temp);// 刷新环境变量putenv(Cwd);
}int CheckBuildIn()
{int yes = 0;const char* enter_cmd = gArgv[0];if (strcmp(enter_cmd, "cd") == 0){yes = 1;Cd();}else if (strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0){yes = 1;printf("%d\n", lastcode);lastcode = 0;}// 其他内建命令...(自己添加咯)return yes;
}int main()
{int quit = 0;while (!quit){// 自己需要输出一个命令行MakeCommandLineAndPrint();// 获取用户命令字符串char usercommand[SIZE];int num = GetUserCommand(usercommand, sizeof(usercommand));if (num < 0) return 1;else if (num == 0) continue;// 分割用户命令字符串SplitCommand(usercommand, sizeof(usercommand));// 检查命令是否为内建命令num = CheckBuildIn();if (num) continue;// 执行命令ExecuteCommmand();}return 0;
}
http://www.dinnco.com/news/5199.html

相关文章:

  • 网站关键词优化工具职业培训机构哪家最好
  • 网站建设论文的部首网络推广违法吗
  • 网站建设生存期模型南京网络推广外包
  • 网站建设的方案费用百度竞价推广怎么样才有效果
  • 网站建设教程菜鸟教程做seo网页价格
  • 腕表手表网站重庆seo公司怎么样
  • 网站开发兼容ie今日新闻头条新闻摘抄
  • 济宁网站建设怎么样贵阳百度推广电话
  • 代网站建设百度网址是多少
  • 如何让自己做的博客网站上线百度权重是什么
  • 广州定制网站建设本周新闻热点事件
  • 公司怎样做网站网站seo外链平台
  • 高明网站设计公司湖北seo
  • wordpress门户建站seo技术推广
  • 南做网站网站优化是什么意思
  • 可信赖的昆明网站建设企业网站推广
  • 专门做字体设计的网站谷歌是如何运营的
  • lol做框网站国内ip地址 免费
  • 做网站需要知道的简单代码站长统计代码
  • 网站建设的行业资讯、凡科网免费建站
  • 品牌网站开发动态模块南京百度推广
  • 公安部门网站建设方案做小程序的公司
  • 网站必须天天更新吗疫情最严重的三个省
  • 2024网站推广网站如何提升seo排名
  • wordpress index.php 跳转惠州短视频seo
  • 怎么做qq分享网站西安网站维护
  • 机械网站 英文市场调研报告3000字范文
  • 介绍做网站的标题2023年时政热点事件
  • 做网站重要标签seo都用在哪些网站
  • 申报课题所需的网站怎么做歌尔股份砍单