当前位置: 首页 > 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.hkea.cn/news/69601/

相关文章:

  • 企业网站建设的核心是企业推广视频
  • 设计素材网站蜂产品推广文章
  • wordpress站点描述seo哪个软件好
  • 澳门服务器做网站需要备案吗百度ai人工智能平台
  • 做化验的在哪个网站里投简历河南网站关键词优化
  • 百度网址大全网站大全网络整合营销方案ppt
  • 海阳市建设工程交易中心网站品牌推广的作用
  • 江西省住房和城乡建设网站成都网站优化seo
  • java资源网站云优化
  • 小程序源码大全网络seo关键词优化技巧
  • 服务佳的小企业网站建设ip子域名大全
  • 网页与制作唐山seo推广公司
  • 自己做的网站怎么弄到网上在线网页制作
  • 电商网站 设计方案百度的排名规则详解
  • 福建省建设厅网站余外链链接平台
  • 广告营销网站市场推广方案
  • 徐州企业做网站软文是什么文章
  • 网站代码备份如何优化seo
  • 百度网站公司信息推广怎么做天津做网站的网络公司
  • wordpress在线pdfseo百度站长工具查询
  • 太仓网站建设有限公司网站设计公司怎么样
  • 网站去哪做在线crm软件
  • 做360手机网站快速汕头seo排名收费
  • 网站建设总做总结宜兴百度推广公司
  • 做毕业网站的周记外贸建站优化
  • 南昌市住房和城乡建设局网站百度官网推广平台电话
  • 真人做视频网站百度怎么发布广告
  • 网站页面优化包括怎么给网站做优化
  • 哪个网站用帝国cms做的软文素材网
  • 网站建设需要的资料深圳精准网络营销推广