Bash:与用户交互的程序——命令解析器
Bash的基本工作流程:{
1.打印提示信息
2.等待用户输入命令
(1)对用户输入的字符串进行拆分
(2)对命令进行分类:内置命令(cd+exit)和外置命令(ls,pwd,ps......)
3.调用fork复制进程,子进程执行用户输入的命令对应的程序
后台:父进程直接执行下一次循环
前台:父进程等待子进程结束
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <assert.h>
#include <sys/utsname.h>
#include <unistd.h>
//1.输出提示信息
void PrintPrompt()
{
struct passwd *pw = getpwuid(getuid());
assert(pw !=NULL);
char flag = '$';
if(getuid() == getpwnam("root")->pw_uid)//如果相等,则当前用户就是root用户
{
flag = '#';
}
struct utsname host; //获取主机信息
uname(&host);
char pwd[128] = {0};
getcwd(pwd,127);
char *p = NULL;
//判断当前工作位置是否是家目录
if(strcmp(pwd,pw->pw_dir) == 0)
{
p = "~";
}
else
{
p = pwd +strlen(pwd);
while(*p != '/')
{
p--;
}
if(strlen(p) > 1) //判断当前工作位置是否是根目录
{
p++;
}
}
printf("[%s@%s %s]%c",pw->pw_name,host.nodename,p ,flag);
}
//2.将命令按照空格进行切割,分别存储到字符指针数组中
void CutCommand(char *cmd,char* CutCmd[])
{
int count = 0;
char *p = strtok(cmd,"");
while(p!=NULL)
{
CutCmd[count++] = p;
p = strtok(NULL,"");
}
}
//仿写cd命令
void PerfomCd(char *path)
{
char newpath[128] = {0};
static char oldpath[128] = {0};
struct passwd* pw = getpwuid(getuid());
assert(pw != NULL);
// 家目录
if (path == NULL)
{
// 给newpath中填充家目录
strcpy(newpath, pw->pw_dir);
}
else if (strncmp(path, "~", 1) == 0)
{
// 给newpath填充家目录后面跟上path的”~“后面的内容
strcpy(newpath, pw->pw_dir);
strcat(newpath, path + 1);
}
else if (strncmp(path, "-", 1) == 0 && strlen(path) == 1)//cd -:切换到上一次的工作目录
{
// 给newpath填充上一次所在位置
if (strlen(oldpath) == 0)
{
printf("cd : OLDPWD not set\n");
return;
}
strcpy(newpath, oldpwd);
}
else
{
//给newpath填充path中的内容
strcpy(newpath, path);
}
char nowpath[128] = { 0 };
getcwd(nowpath, 127);
if (-1 == chdir(newpath))
{
perror(path);
return;
}
memset(oldath, 0, 128);
strcpy(oldpath, nowpath);
}
//3.分类命令,如果是内置命令则交给后面的流程处理
int ClassOfCommand(char *CutCmd[])
{
if(strncmp(CutCmd[0],"exit",4) == 0)//比较,如果相等
{
exit(0);
}
if(strncmp(CutCmd[0],"cd",2) == 0)//比较,如果相等
{
PerfomCd(CutCmd[1]);
return 1; //内置命令
}
return 0; //外置命令
}
//处理外置命令
void PerformOutCommand(char *CutCmd[])
{
pid_t pid = fork();
assert(pid != -1);
if(pid == 0) //子进程执行用户输入的命令对应的程序
{
char path[128] = "/home/stu/hw/mybin/";
//char path[128] = "/bin/"; //测试用
if (strstr(CutCmd[0], "/") != NULL) // 用户输入的执行命令中有路径
{
memset(path, 0, 128);
}
strcat(path, CutCmd[0]);
execv(path, CutCmd); // 进程替换
perror("execv : ");
exit(0); // 当execv替换失败后,子进程必须结束
}
else
{
wait(NULL);
}
}
int main()
{
while(1)
{
PrintPrompt(); // 1. 输出提示信息
char cmd[128]={0}; //得到用户输入的命令
fgets(cmd,127,stdin);
cmd[strlen(cmd) -1] = 0; //去掉最后的回车符
if(strlen(cmd) == 0) //处理用户只是输入了回车的情况
{
continue;
}
char *CutCmd[10] = {0};
CutCommand(cmd,CutCmd); //处理切割后的命令
if(ClassOfCommand(CutCmd))
{
continue;
}
//如果能执行到此,说明用户执行的是外置命令
PerformOutCommand(CutCmd);
}
}