
前言:
通过我们之前对Linux的学习,今天来利用所学知识做出来一个可执行程序(一个简单的shell)。
一.程序所需要用到的工具
1.getcmd:返回当前工作目录的路径。
char *getcwd(char *buf, size_t size);
他的两个参数分别是所存储的空间以及buf的空间大小。
2.snprintf
int snprintf(char *str, size_t size, const char *format, ...);
char *str:指向目标缓冲区的指针,用于存储生成的字符串。
size_t size:最关键的参数 。指定缓冲区的大小(能存放的最大字符数,包括结尾的\0)。函数最多会写入size - 1个字符,然后在末尾自动添加\0。const char *format:格式化字符串(和printf用法完全一样,如"Hello %s")。
...:可变参数,要格式化的变量。
主要的作用是将想要在屏幕上打出的内容放到str中,str为自己常见的一个数组。size为这个数组的大小。
3.std::string::npos用于检查在一个字符串中是否没有找到指定的字符或子串一般与rfind搭配使用,如果rfind没有找到则会返回npos。
4.find和rfind
find 和 rfind 都是 C++ std::string 中用于查找的成员函数,主要区别在于查找方向 和返回结果。
主要区别
| 特性 | find() |
rfind() |
|---|---|---|
| 查找方向 | 从左到右(正向) | 从右到左(反向) |
| 返回位置 | 第一次出现的位置 | 最后一次出现的位置 |
| 默认起始位置 | 从索引0开始 | 从字符串末尾开始 |
5.substr
string substr(size_t pos = 0, size_t count = npos) const;
substr 是 C++ std::string 中的一个成员函数 ,用于提取字符串中的子串。
-
参数1
pos:起始位置(默认从0开始) -
参数2
count:要提取的字符数(默认到字符串末尾) -
返回值:提取出的新字符串
6.fgets()
char *fgets(char *str, int n, FILE *stream);
fgets 是 C 语言标准库中的一个文件输入函数 ,用于从文件流中读取一行字符串。
-
str:存储读取内容的字符数组(缓冲区) -
n:最多读取的字符数(包括最后的\0) -
stream:文件流指针(如stdin或打开的文件)
7.strtuk
char *strtok(char *str, const char *delim);
-
str:要分割的字符串(第一次调用)(当多次分割同一个字符串时第二次之后传的参数为nullptr) -
delim:分隔符集合(字符串,包含所有可能的分隔字符) -
返回值 :指向当前找到的令牌的指针,如果没有更多令牌则返回
NULL
8.waitpid
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
-
pid:指定要等待的进程-
> 0:等待指定 PID 的子进程 -
-1:等待任意子进程(同wait) -
0:等待与调用进程同一进程组的任意子进程 -
< -1:等待进程组 ID 为|pid|的任意子进程
-
-
status:存储子进程退出状态的指针(可为NULL) -
options:控制等待行为的选项(常用:WNOHANG、WUNTRACED)(一般阻塞等待时传0即可,WNOHANG为非阻塞时等待)
| 宏 | 作用 | 使用条件 |
|---|---|---|
WIFEXITED(status) |
检查是否正常退出 | 总是先调用 |
WEXITSTATUS(status) |
获取退出码 | WIFEXITED 为真时 |
返回值
-
成功:返回状态改变的子进程 PID
-
WNOHANG且无子进程退出 :返回0 -
出错 :返回
-1
9.chdir
#include <unistd.h>
int chdir(const char *path);
chdir 是 UNIX/Linux 系统中用于改变当前工作目录的系统调用。
参数说明
path:要切换到的目录路径(绝对路径或相对路径)
返回值
-
成功 :返回
0 -
失败 :返回
-1,并设置errno错误码
10.environ
environ 是一个全局变量 ,用于访问进程的环境变量 。它是一个字符串数组,每个字符串都是 "key=value" 格式,以 NULL 结尾。
二.正文代码
cpp
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<unistd.h>
#include<cstring>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
const int basesize=1024;
const int garsum=64;
const int envnum=64;
char *gargv[garsum];
int gargc=0;
int lastcode=0;
char*genv[envnum];
char* pwd[basesize];
char* pwdnev[basesize];
string GetUserName()
{
string Username=getenv("USER");
return Username.empty()?"done":Username;
}
string HostName()
{
string Hostname=getenv("HOSTNAME");
return Hostname.empty()?"none":Hostname;
}
string Getpwd()
{
// if(nullptr==getcmd(pwd,sizeof(pwd)))return "Done";
string Mypwd=getenv("PWD");
return Mypwd.empty()?"done":Mypwd;
}
string Lastdir()
{
string str=Getpwd();
if(str=="/"||str=="done")return str;
size_t jud=str.rfind("/");
if(jud==std::string::npos)return str;
return str.substr(jud+1);
}
string Makecommandline()
{
char comandline[basesize];
snprintf(comandline,sizeof(comandline)-1,"[%s@%s %s]#",GetUserName().c_str(),HostName().c_str(),Lastdir().c_str());
return comandline;
}
void PriontComLine()
{
printf("%s",Makecommandline().c_str());
fflush(stdout);
}
bool GetcommanLine(char buffer[],int size)
{
char*pos= fgets(buffer,size,stdin);
if(!pos)
{
perror("fgets");
return false;
}
buffer[strlen(buffer)-1]=0;
if(strlen(buffer)==0)return false;
return true;
}
void AnalyCommanLine(char command_buffer[],int size)
{
(void)size;
memset(gargv,0,sizeof(gargv));
gargc=0;
const char* flag=" ";
gargv[gargc++]=strtok(command_buffer,flag);
while ((bool)( gargv[gargc++]=strtok(nullptr,flag)));
gargc--;
}
void debug()
{
int i=0;
for( i=0;gargv[i];i++)
{
printf("%s\n",gargv[i]);
}
}
bool ExecuteCommand()
{
pid_t id=fork();
if(id<0)
{
return false;
}
if(id==0)
{
//子进程
execvp(gargv[0],gargv);
exit(1);
}
int statue=0;
pid_t rid=waitpid(id,&statue,0);
if(rid>0)
{
//等待成功
if(WIFEXITED(statue))
{
lastcode=WEXITSTATUS(statue);
}
else{
lastcode=100;
}
return true;
}
return false;
}
void Addenv(const char * item)
{
int index=0;
while(genv[index])
{
index++;
}
genv[index]=(char*)malloc(strlen(item)+1);
strncpy(genv[index],item,strlen(item)+1);
genv[++index]=nullptr;
}
bool CheckCommand()
{
if(strcmp(gargv[0],"cd")==0)
{
if(gargc==2)
{
chdir(gargv[1]);
lastcode=0;
}
else{
lastcode=1;
}
return true;
}
else if(strcmp(gargv[0],"export")==0)
{
if(gargc==2)
{
Addenv(gargv[1]);
lastcode=0;
}
else{
lastcode=1;
}
return true;
}
else if(strcmp(gargv[0],"env")==0)
{
int index=0;
printf("%s\n",genv[index]);
while(genv[index])
{
printf("%s\n",genv[index]);
index++;
}
lastcode=0;
return true;
}
return false;
}
void InintEnv()
{
extern char**environ;
int index=0;
while(environ[index])
{
genv[index]=(char*)malloc(strlen(environ[index])+1);
strncpy(genv[index],environ[index],strlen(environ[index])+1);
index++;
}
genv[index]=nullptr;
}
int main()
{
InintEnv();
char command_buffer[basesize];
while(true)
{
//打印命令行
PriontComLine();
//获取用户指令
if(!GetcommanLine(command_buffer,basesize))
{
continue;
}
AnalyCommanLine(command_buffer,strlen(command_buffer));
//debug();
if(CheckCommand())
{
continue;
}
ExecuteCommand();
}
return 0;
}