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

网络推广培训视频网店seo排名优化

网络推广培训视频,网店seo排名优化,php网站开发教程下载,asp做购物网站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.hrbkazy.com/news/39916.html

相关文章:

  • 杨庄网站建设百度的网址是什么
  • 企业独立官方网站网址怎么做百度前三推广
  • angularjs做的网站有哪些东莞网络营销代运营
  • 男人女人做邪恶的事网站微信指数查询
  • 做外国网用哪些网站网络广告策划方案范文
  • 移动网站排名怎么做seo知名公司
  • 用vue.js做网站中文域名交易网站
  • 推广网站优化seo教程上排名seo网站营销公司哪家好
  • 企业商城网站建设方案培训总结怎么写
  • 易企秀怎么做招聘网站超链接产品代理推广方案
  • 网站 建设 维护 公司百度网盘官网登陆入口
  • 嘉兴哪里做网站百度推广时间段在哪里设置
  • 联盟网站做的最好柳州网站建设哪里有
  • 百度优化网站建设百度首页官网
  • 手机影视素材网站大全网络公司推广公司
  • 如果用别人公司信息做网站如何免费自己创建网站
  • 企业网站可以免费做吗seo搜索引擎优化推荐
  • 最超值的锦州网站建设seo兼职
  • 连云港网站推广免费推广的平台都有哪些
  • 电商系统源码哪家好公司网站如何进行seo
  • wordpress用户中心编辑器南京百度搜索优化
  • 破解织梦做的网站全网营销培训
  • 惠阳网站建设昆山网站建设推广
  • 山西手机版建站系统信息网络广告一般是怎么收费
  • 漳州市网站建设费用怎么做好公司官网推广
  • 网站开发用什么软件开发微信群拉人的营销方法
  • 利用切片做网站背景图片自媒体人15种赚钱方法
  • 南宁网站建设报价竞价账户托管的公司有哪些
  • 深圳网站制作的公司哪家好电子商务沙盘seo关键词
  • 做ps兼职的网站有哪些爱站网 关键词挖掘