shell简易版编写

2024-08-29 07:04
文章标签 编写 shell 简易版

本文主要是介绍shell简易版编写,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、制造命令行提示符

二、获取命令

三、分割命令

四、执行命令

五、内建命令

全部代码


一、制造命令行提示符

命令行主要有三个组成:用户名、主机名和路径。这也是需要优先获取的。

将每块功能封装起来,这样阅读性高一些。

这三个都存放在环境变量里,可以通过getenv去获取。

对应的环境变量:

  • 用户名:USER
  • 主机名:HOSTNAME
  • 路径:PWD

将这三个字符串放入到line中,使用snprintf函数。

命令行是不能加\n的,我们要在命令行后面输出内容,通过刷新缓冲区的方式来实现。

scanf代替一下获取命令,能更好看出制造命令行的效果。原命令行是$结尾,这里使用>结尾做区分。

基本和原命令行是一样的,不过路径因该是只保留最后一个,后面再对这个进行更改。

二、获取命令

输入内容使用fgets,它可以以\n为结束读取的字符。

可以看到确实读到了空格,但打印的usercommand是多了一行,%s\n因该是只换行一次。这是因为fgets把\n也读了进去,我们需要把字符串最后一个字符给改一下(\n是一个字符,不是\和n两个字符)。

在输入命令时输入错误,需要Ctrl+Backspace才能删除。

三、分割命令

命令需要分割成argv字符串数组一样进行使用。定义一个全局的Argv存放。

for是为了测试。分割字符串有对应的函数strtok来分割。

使用while进行这样的循环赋值,strtok再分割完后会返回NULL,刚好符合Argv以NULL结尾,也可以时循环停止。这里多加一层()是为了不报wrning。

四、执行命令

只执行PATH内的可执行文件,使用execvp替换函数。再将这几个功能一起循环就有一个简易shell效果了。

现在就能完成一些PATH内的功能,不过对于内建命令还不能使用。

五、内建命令

内建命令指的是那些shell不需要创建子进程的命令,内建命令就是shell内的一段代码。如cd、echo [环境变量]等。

cd测试:

cd是改变当前的工作目录,如果是让子进程来执行那就是子进程的工作目录发生了改变,父进程的并没有。echo $[环境变量]也是子进程查看的是自己的环境变量,不是父进程的,父进程的内建命令在使用时会改变一些环境变量需要自己维护。

单独判断内建命令

内建命令的判断非常朴实,有几个就使用几个if。

echo $?:拿到最近一次进程的退出信息。只需要将lastcode设为全局变量,就可以拿到上一次的退出信息,需要注意在用户的角度echo也是在执行文件,所以需要将lastcode值改为0,视作echo的退出信息。

cd:更改路径可以使用chdir,修改完路径环境变量PWD也需要跟着修改,不过不能直接拿输入的路劲来修改,PWD需要绝对路径,cd会有一些特殊的符号来表示一些路劲(~:家目录;-:上一次所处路劲;不跟任何路劲和符号:家目录),这个需要单独处理。更改路劲后先通过getcwd拿到当前路径,再将当前路劲以环境变量的形式存起来。

运行结果:

现在在来处理一下命令行提示符中路劲的问题,要只显示当前所属目录名。

要在获取路劲后修改,找最后一个目录。只需要从字符串尾向前找到第一个/就可以,使用一个宏函数来封装。

使用{}是为了让宏函数在使用时,能在结尾加;看起来规范些。

原shell在命令行是没有/的,打印路劲时也只打印/后面的内容,cwd+1就可以。不过这会导致在跟目录下来回打印/需要单独判断。

运行结果:

全部代码

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<errno.h>#define Size 512
#define SkipPath(p) { p += (strlen(p)-1); while(*p != '/') p--;}
char* Argv[32];
char cwd[Size*2];
int lastcode;const char* GetUserName()
{const char* name = getenv("USER");if(name == NULL) return "None";return name;
}const char* GetHomeName()
{const char* name = getenv("HOSTNAME");if(name == NULL) return "None";return name;
}const char* GetCwd()
{const char* cwd = getenv("PWD");if(cwd == NULL) return "None";return cwd;
}const char* GetHome()
{const char* home = getenv("HOME");if(home == NULL) return "/";return home;
}void MakeCommandLineAndPrint()
{char line[Size];//存放命令行//获取用户名const char* username = GetUserName();//获取主机名const char* hostname = GetHomeName();//获取路径const char* cwd = GetCwd();SkipPath(cwd);//让cwd去指向最后一个/的位置//结合起来并打印snprintf(line,sizeof(line),"[%s@%s %s]> " ,username, hostname, strlen(cwd)==1 ? "/" : cwd+1);printf("%s",line);fflush(stdout);
}int GetUserCommand(char* usercommand, size_t size)
{if(fgets(usercommand, size, stdin) == NULL){return -1;}usercommand[strlen(usercommand)-1] = '\0';return strlen(usercommand);
}void SplitCommand(char* usercommand)
{Argv[0] = strtok(usercommand," ");int i = 1;while((Argv[i++] = strtok(NULL," ")));
}void ExecuteCommand()
{pid_t id = fork();if(id < 0){perror("fork");//创建失败exit(1);}else if(id == 0){execvp(Argv[0],Argv);//去PATH中查找可执行文件exit(errno);//子进程走到这里就表示替换失败,也就是命令执行失败}int status = 0;pid_t rid = waitpid(id,&status,0);if(rid>0){lastcode = WEXITSTATUS(status);if(lastcode != 0){//命令执行错误,打印错误信息printf("%s:%s:%d\n", Argv[0], strerror(lastcode), lastcode);}}
}void Cd()
{const char *path = Argv[1];if(path == NULL || strcmp(path,"~") == 0) path = GetHome();//处理特殊符// 这时path 一定存在路劲chdir(path);//更改路径char temp[Size*2];getcwd(temp, sizeof(temp));//拿到当前绝对路劲//cwd是保存的全局变量,除了env内要有绝对路劲,程序内部也需要保存使用snprintf(cwd, sizeof(cwd), "PWD=%s", temp);putenv(cwd); // OK
}int CheckBuildin()
{char* s = Argv[0];int ret = 0;if(strcmp(s,"cd") == 0){ret = 1;Cd();}else if(strcmp(s,"echo") == 0 && strcmp(Argv[1],"$?") == 0){ret = 1;printf("%d\n", lastcode);lastcode = 0;}return ret;
}int main()
{while(1){//制造命令行MakeCommandLineAndPrint();//获取命令char usercommand[Size];int n = GetUserCommand(usercommand,sizeof(usercommand));//将命令放入usercommandif(n == -1) return 1;//获取失败//分割命令SplitCommand(usercommand);//判断是否是内建命令if(CheckBuildin())//是内建返回真,会在函数内处理执行{continue;}//执行命令ExecuteCommand();}return 0;
}

这篇关于shell简易版编写的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1117177

相关文章

Linux中shell解析脚本的通配符、元字符、转义符说明

《Linux中shell解析脚本的通配符、元字符、转义符说明》:本文主要介绍shell通配符、元字符、转义符以及shell解析脚本的过程,通配符用于路径扩展,元字符用于多命令分割,转义符用于将特殊... 目录一、linux shell通配符(wildcard)二、shell元字符(特殊字符 Meta)三、s

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

shell脚本快速检查192.168.1网段ip是否在用的方法

《shell脚本快速检查192.168.1网段ip是否在用的方法》该Shell脚本通过并发ping命令检查192.168.1网段中哪些IP地址正在使用,脚本定义了网络段、超时时间和并行扫描数量,并使用... 目录脚本:检查 192.168.1 网段 IP 是否在用脚本说明使用方法示例输出优化建议总结检查 1

使用Java编写一个文件批量重命名工具

《使用Java编写一个文件批量重命名工具》这篇文章主要为大家详细介绍了如何使用Java编写一个文件批量重命名工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录背景处理1. 文件夹检查与遍历2. 批量重命名3. 输出配置代码片段完整代码背景在开发移动应用时,UI设计通常会提供不

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo

jenkins 插件执行shell命令时,提示“Command not found”处理方法

首先提示找不到“Command not found,可能我们第一反应是查看目标机器是否已支持该命令,不过如果相信能找到这里来的朋友估计遇到的跟我一样,其实目标机器是没有问题的通过一些远程工具执行shell命令是可以执行。奇怪的就是通过jenkinsSSH插件无法执行,经一番折腾各种搜索发现是jenkins没有加载/etc/profile导致。 【解决办法】: 需要在jenkins调用shell脚

Wondows dos下怎么编写bat批处理文件

最近搞php,在运行时,以Nginx+php-cgi.exe方式运行Wordpress项目 打开dos,先cd到php-cgi.exe文件当前目录下执行启动命令:php-cgi.exe -b 127.0.0.1:9001再打开一个dos,再cd到nginx.exe文件当前目录下执行启动命令:start nginx 大概过程要经过这些步骤,觉得很麻烦,就学下怎么编写一个bat文件,以双击运行代替

站长常用Shell脚本整理分享(全)

站长常用Shell脚本整理分享 站长常用Shell脚本整理分享1-10 站长常用Shell脚本整理分享11-20 站长常用Shell脚本整理分享21-30 站长常用Shell脚本整理分享31-40 站长常用Shell脚本整理分享41-50 站长常用Shell脚本整理分享51-59 长期更新

Shell脚本实现自动登录服务器

1.登录脚本 login_server.sh #!/bin/bash# ReferenceLink:https://yq.aliyun.com/articles/516347#show all host infos of serverList.txtif [[ -f ./serverList.txt ]]thenhostNum=`cat ./serverList.txt | wc -l`e