简单shell命令行解释器的设计与实现
思路&分析
1.背景知识
本实验要使用创建子进程的fork()函数,执行新的命令的exec ()系列函数,通常shell是等待子进程结束后再接受用户新的输入,这可以使用waitpid()函数。
2.设计步骤
提示:shell的主体就是反复下面的循环过程
while(1){
接收用户输入的命令行;
解析命令行;
if(用户命令为内部命令)
直接处理;
elseif(用户命令为外部命令)
创建子进程执行命令;
else
提示错误的命令;
}
3.根据设计步骤逐步完成各个命令
-
首先是接受键盘的输入数据,这里我们采用一个字符串数组,一个一个字符读入,赋值给该字符串数组。
-
解析命令行:将输入数据按照空格分开,第一个字符串为命令,之后几个为参数。
-
根据第一个字符串参数判断是什么命令。
-
若为echo,后面有数据则输出。
-
若为cd,则利用chdir()函数切换目录,然后通过自己写的pwd()函数获取当前目录输出。
-
若为help命令,直接输出相关内容。
-
environ,则通过fork()创建子进程,利用函数exec()执行,env命令。
-
jobs,自己写一个函数获取当前shell的进程名和进程ID,Linux下的当前正在运行的进程信息均存放在/proc目录下,有一系列以数字为名的子目录,每一个子目录对应一个进程,子目录名就是进程的pid。子目录下的status文件内容就是进程的基本信息,包括进程名、pid、ppid等。因此,若要扫描系统当前正在运行的所有进程,只需要遍历/proc目录下所有以数字为名的子目录下的status即可。据此,可得到获取当前系统所有进程快照、根据pid获得进程名和根据进程名获得pid的程序。该命令参照博客链接
-
quit,bye,exit,直接用exit()即可。
结果
结果截图一:help,echo,cd,pwd
结果截图二:jobs,envirn,bye
代码(linux执行下)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pwd.h>
#include <dirent.h>
#define BUF_SIZE 1024
#define MAX_PROCESS_PATH 1024
#define PROC_NAME_FIELD ("name:")
#define PARENT_PID_FIELD ("ppid:")
#ifndef __cplusplus
typedef enum {false, true} bool;
#endif // __cplusplus
typedef struct __tagProcessEntry
{
pid_t pid;
pid_t ppid;
char processName[MAX_PROCESS_PATH];
} ProcessEntry;
void string2lower(const char src[], char dest[])
{
int length = strlen(src);
int index;
for(index = 0; index < length; index ++)
{
dest[index] = ((src[index] >= 'A') && (src[index] <= 'Z')) ? src[index] + 0x20 : src[index];
}
dest[length] = '\0';
}
int createProcessSnapshot(ProcessEntry pes[], int maxCount)
{
int processCount = 0;
DIR *dir;
struct dirent *dirEntry;
FILE *fp;
char filePath[MAX_PROCESS_PATH];
char fieldName[32];
char fieldValue[MAX_PROCESS_PATH];
char buf[BUF_SIZE];
dir = opendir("/proc");
if (NULL != dir)
{
while((dirEntry = readdir(dir)) != NULL) //循环读取路径下的每一个文件/文件夹
{
//如果读取到的是"."或者".."则跳过,读取到的不是文件夹名字也跳过
if ((strcmp(dirEntry->d_name, ".") == 0) || (strcmp(dirEntry->d_name, "..") == 0) || (DT_DIR != dirEntry->d_type))
{
continue;
}
sprintf(filePath, "/proc/%s/status", dirEntry->d_name); //生成要读取的文件的路径
fp = fopen(filePath, "r");
if (NULL != fp)
{
do {
if (fgets(buf, BUF_SIZE - 1, fp) == NULL)
{
break;
}
sscanf(buf, "%s %s", fieldName, fieldValue);
string2lower(fieldName, fieldName);
if (strcmp(fieldName, PROC_NAME_FIELD) == 0)
{
strcpy(pes[processCount].processName, fieldValue);
}
else if (strcmp(fieldName, PARENT_PID_FIELD) == 0)
{
pes[processCount].ppid = atoi(fieldValue);
}
} while(!feof(fp));
fclose(fp);
pes[processCount].pid = atoi(dirEntry->d_name);
processCount ++;
if (processCount >= maxCount)
{
break;
}
}
}
closedir(dir);
}
return processCount;
}
int getPidByName(const char *procName, pid_t pids[], int maxCount)
{
DIR *dir;
struct dirent *dirEntry;
FILE *fp;
char filePath[MAX_PROCESS_PATH];
char currProcName[MAX_PROCESS_PATH];
char buf[BUF_SIZE];
int pidCount = 0;
dir = opendir("/proc");
if (NULL != dir)
{
while((dirEntry = readdir(dir)) != NULL) //循环读取路径下的每一个文件/文件夹
{
//如果读取到的是"."或者".."则跳过,读取到的不是文件夹名字也跳过
if ((strcmp(dirEntry->d_name, ".") == 0) || (strcmp(dirEntry->d_name, "..") == 0) || (DT_DIR != dirEntry->d_type))
{
continue;
}
sprintf(filePath, "/proc/%s/status", dirEntry->d_name); //生成要读取的文件的路径
fp = fopen(filePath, "r"); //打开进程描述文件
if (NULL != fp)
{
if (fgets(buf, BUF_SIZE - 1, fp) == NULL)
{
fclose(fp);
continue;
}
sscanf(buf, "%*s %s", currProcName);
if (strcmp(procName, currProcName) == 0)
{
pids[pidCount] = atoi(dirEntry->d_name);
pidCount ++;
if (pidCount >= maxCount)
{
break;
}
}
fclose(fp);
}
}
closedir(dir);
}
return pidCount;
}
bool getNameByPid(pid_t pid, char procName[])
{
char pidPath[BUF_SIZE];
char buf[BUF_SIZE];
bool success = false;
sprintf(pidPath, "/proc/%d/status", pid);
FILE *fp = fopen(pidPath, "r");
if (NULL != fp)
{
if (fgets(buf, BUF_SIZE - 1, fp) != NULL)
{
sscanf(buf, "%*s %s", procName);
success = true;
}
else
{
success = false;
}
fclose(fp);
}
return success;
}
void print()
{
struct passwd *usrname;
usrname = getpwuid(getuid());
char buf[256];
memset(buf,0,sizeof(buf));
printf("[my_shell:%s]",getcwd(buf,256));
if(strcmp(usrname->pw_name,"root")==0)
printf("#");
else
printf("$");
}
int shellJudgment(char *arglist[],int args_num)
{
if(strcmp(arglist[0],"cd")==0||strcmp(arglist[0],"pwd")==0)
return 1;//cd
else if(strcmp(arglist[0],"help")==0&&(args_num==1))//help
return 2;
else if(strcmp(arglist[0],"environ")==0&&(args_num==1))//environ
return 3;
else if(strcmp(arglist[0],"echo")==0)//echo
return 4;
else if(strcmp(arglist[0],"jobs")==0)//jobs
return 5;
else if((strcmp(arglist[0],"quit")==0)||(strcmp(arglist[0],"exit")==0)||(strcmp(arglist[0],"bye")==0)&&(args_num==1))//bye
return 6;
else
return 0;
return 0;
}
void pwd()
{
char buf[1024];
char *cwd =getcwd(buf, sizeof(buf));
if (NULL == cwd) {
perror("Get cerrent working directory fail.\n");
exit(-1);
} else {
printf("Current working directory is : %s\n", cwd);
}
}
void printPID()
{
char procName[MAX_PROCESS_PATH];
pid_t pids[32];
ProcessEntry pes[1024];
pid_t currPid = getpid();
printf("pid of this process:%d\n", currPid);
getNameByPid(currPid, procName);
printf("task name is %s\n", procName);
strcpy(procName, "eclipse");
int pidCount= getPidByName(procName, pids, sizeof pids / sizeof pids[0]);
int index=0;
for(index = 0; index < pidCount; index ++)
{
printf("pid of process %s: %d\n", procName, pids[index]);
}
int procCnt = createProcessSnapshot(pes, sizeof pes / sizeof pes[0]);
printf("%d processes found.\n", procCnt);
for(index = 0; index < procCnt; index ++)
{
printf("processName: %-16s, pid: %6d\n", pes[index].processName, pes[index].pid);
}
}
int main()
{
char cmd[512];
int flag,i,k,pid;
char c;
while(1){
print();
k=0;
//-----------------read command-----------------
while(1)
{
c=getchar();
if(c!='\n')
cmd[k++]=c;
else
{
cmd[k++]='\0';
break;
}
}
//-----------------read command-----------------------
//------------------Parsing command--------------------
char *arglist[20]={0};
char *cp=cmd;
int args_num=0;
int len;
char *start;
while(*cp!='\0')
{
while(*cp==' '||*cp=='\t')
cp++;
start=cp;
len=1;
c=*++cp;
while(c!='\0'&&c!='\t'&&c!=' ')
{
len++;
c=*++cp;
}
arglist[args_num]=(char *)malloc(len+1);
strncpy(arglist[args_num],start,len);
arglist[args_num][len]='\0';
args_num++;
}
//---------Parsing command-----------------------------
flag=0;
flag=shellJudgment(arglist,args_num);
//cd
if(flag==1)
{
if(strcmp(arglist[0],"cd")==0)
{
if(args_num==1)
pwd();
else if(args_num==2)
{
if(chdir(arglist[1])==-1)
printf("error directory\n");
}
}
else
pwd();
}
//---------help----------------------------------------
else if(flag==2)
{
printf("______________________________________________________________________________\n");
printf("The following are the commands and functions of my shell\n");
printf("command function\n");
printf("______________________________________________________________________________\n");
printf("cd Change the current working directory to another directory\n");
printf("environ List all environment variable string settings\n");
printf("echo<content> Display the content after echo and wrap\n");
printf("help Display the ways and functions of my shell\n");
printf("jobs Output the current series of sub-processes of the shell, the command and PID of the sub-process must be provided\n");
printf("quit/exit/bye Exit the shell\n");
printf("______________________________________________________________________________\n");
}
//-----------------echo---------------------------------------
else if(flag==4)
{
if(args_num==2){
printf("%s",arglist[1]);
}
printf("\n");
}
//-----------------Bye----------------------------------------------
else if(flag==6)
{
printf("Bye\n");
for(i=0;i<args_num;i++)
{
free(arglist[i]);
}
return 0;
}
else if(flag==0)
printf("error command!\n");
//----------------- environ jobs-----------------------------
else if(flag==5)//jobs
{
printPID();
}
else
{
pid_t pid;
/* fork a child process */
pid = fork();
if (pid < 0)
{ /* error occurred */
fprintf(stderr, "Fork Failed!");
return 1;
}
else if (pid == 0)
{
if(flag==3)//environ
{
execlp("env","",NULL);
return 0;
}
}
else
{
wait(NULL);
}
}
}
return 0;
}