一、使用系统命令的完整代码
#include<stdio.h>
#include<wait.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<pwd.h>#define ARG_MAX 10//防止参数不够,可以做到一改全改char *get_cmd(char *buff,char *myargv[]){//这个函数的作用是分割命令if(buff == NULL||myargv == NULL){return NULL;//说明用户没有输入命令}char *s = strtok(buff," ");//第一次调用strtokint i = 0;while(s!=NULL){//不确定用户输入的命令需要分割几次,只要分割到字符串末尾为'\0',就会结束myargv[i++] = s;//保存分割的结果s = strtok(NULL," ");//后续调用不用再传buff,因为内部有指针会记录}return myargv[0];
}void run_cmd(char *path,char *myargv[]){//这个函数的作用是当输入命令为普通命令时,创建子进程执行if(path == NULL || myargv == NULL)return;pid_t pid = fork();if(pid == -1)return;//下面我们要让父子进程做不一样的事情if(pid == 0)//子进程进入if{execvp(path,myargv);//这里选择替换系列时,选带v,这样就可以传数组,选带p的,这样就不需要传环境变量perror("execvp error!\n");//一定要记得打印错误信息,这样方便我们明确的知道execvp是否执行成功// exit(0);}else//父进程进入esle{//这里要记得处理将死进程wait(NULL);}
}void printf_info(){//打印提示信息,获取用户名,主机名,当前位置,获取用户角色(普通用户还是管理员)char *user_str="$";//默认为普通用户int user_id = getuid();if(user_id == 0){//当uid=0时为root,将$改为#user_str = "#";}struct passwd * ptr = getpwuid(user_id);//得到用户名if(ptr == NULL)//如果为NULL,也就是出现问题了,那么打印bash的版本号{printf("mybash1.0>> ");fflush(stdout);return;}//获取主机名char hostname[128] = {0};if(gethostname(hostname,128)==-1){//如果获取主机名失败,打印版本号printf("mybash1.0>> ");fflush(stdout);return;}//获取主机路径char dir[256] = {0};if(getcwd(dir,256)==NULL){//如果获取路径失败,打印版本号printf("mybash1.0>> ");fflush(stdout);return;}printf("\033[1;32m%s@%s\033[0m \033[1;34m%s\033[0m%s ",ptr->pw_name,hostname,dir,user_str);fflush(stdout);
}int main()
{while(1){printf_info();char buff[128]={0};fgets(buff,128,stdin);buff[strlen(buff)-1] = '\0';//去除buff里面的\n,防止exit和exit\n不匹配char *myargv[ARG_MAX] = {0};char *cmd = get_cmd(buff,myargv);//提取buff里面的命令if(cmd == NULL){continue;}else if(strcmp(cmd,"cd")==0){if(myargv[1] != NULL){//当cd后面有路径时if(chdir(myargv[1])==-1){perror("cd err!\n");}}//当用户只输入cd时,会进入家目录(和系统保持一致)}else if(strcmp(cmd,"exit")==0){break;//若为exit,则退出内部exit(0);//可以,但不好}else{//普通命令//fork + execrun_cmd(cmd,myargv);//也可以直接传myargv}}exit(0);
}
二、使用自己环境变量的完整代码
//mybash.c
#include<stdio.h>
#include<wait.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<pwd.h>#define ARG_MAX 10//防止参数不够,可以做到一改全改
#define PATH_BIN "/home/stu/quzijie/class03/test15/mybin/"char *get_cmd(char *buff,char *myargv[]){//这个函数的作用是分割命令if(buff == NULL||myargv == NULL){return NULL;//说明用户没有输入命令}char *s = strtok(buff," ");//第一次调用strtokint i = 0;while(s!=NULL){//不确定用户输入的命令需要分割几次,只要分割到字符串末尾为'\0',就>会结束myargv[i++] = s;//保存分割的结果s = strtok(NULL," ");//后续调用不用再传buff,因为内部有指针会记录}return myargv[0];
}void run_cmd(char *path,char *myargv[]){//这个函数的作用是当输入命令为普通命令时,创>建子进程执行if(path == NULL || myargv == NULL)return;pid_t pid = fork();if(pid == -1)return;//下面我们要让父子进程做不一样的事情if(pid == 0)//子进程进入if{//使用自己的环境变量mybin//在只输入命令的情况下拼接,./和/完整路径这两种情况都不需要拼接char pathname[128] = {0};if(strncmp(path,"/",1)==0||strncmp(path,"./",2)==0)//不拼接{strcpy(pathname,path);}else//拼接,只有命令{strcpy(pathname,PATH_BIN);//先复制路径strcat(pathname,path);//然后和命令拼接}execv(pathname,myargv);perror("execv error!\n");exit(0);}else//父进程进入esle{//这里要记得处理将死进程wait(NULL);}
}
void printf_info(){//打印提示信息,获取用户名,主机名,当前位置,获取用户角色(普通用
户还是管理员)char *user_str="$";//默认为普通用户int user_id = getuid();if(user_id == 0){//当uid=0时为root,将$改为#user_str = "#";}struct passwd * ptr = getpwuid(user_id);//得到用户名if(ptr == NULL)//如果为NULL,也就是出现问题了,那么打印bash的版本号{printf("mybash1.0>> ");fflush(stdout);return;}//获取主机名char hostname[128] = {0};if(gethostname(hostname,128)==-1){//如果获取主机名失败,打印版本号printf("mybash1.0>> ");fflush(stdout);return;}//获取主机路径char dir[256] = {0};if(getcwd(dir,256)==NULL){//如果获取路径失败,打印版本号printf("mybash1.0>> ");fflush(stdout);return;}printf("\033[1;32m%s@%s\033[0m \033[1;34m%s\033[0m%s ",ptr->pw_name,hostname,dir,user_str);fflush(stdout);
}
int main()
{while(1){printf_info();char buff[128]={0};fgets(buff,128,stdin);buff[strlen(buff)-1] = '\0';//去除buff里面的\n,防止exit和exit\n不匹配char *myargv[ARG_MAX] = {0};char *cmd = get_cmd(buff,myargv);//提取buff里面的命令if(cmd == NULL){continue;}else if(strcmp(cmd,"cd")==0){if(myargv[1] != NULL){//当cd后面有路径时if(chdir(myargv[1])==-1){perror("cd err!\n");}}//当用户只输入cd时,会进入家目录(和系统保持一致)}else if(strcmp(cmd,"exit")==0){break;//若为exit,则退出内部exit(0);//可以,但不好}else{//普通命令//fork + execrun_cmd(cmd,myargv);//也可以直接传myargv}}exit(0);
}
//mybin目录下的自己实现的命令
//clear.c
include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int main(){printf("\033[2J\033[0;0H");
}
//pwd.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int main(){char path[256] = {0};if(getcwd(path,256) == NULL) { perror("getcwd error!\n");exit(1);} printf("%s\n",path);exit(0);
}
//ls.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<dirent.h>
#include<sys/stat.h>
int main(){char path[256] = {0};if(getcwd(path,256) == NULL){perror("getcwd error!\n");exit(1);} DIR *pdir = opendir(path);if(pdir == NULL){ perror("opendir error!\n");exit(1);} struct dirent *s = NULL;while((s=readdir(pdir))!=NULL){ if(strncmp(s->d_name,".",1)==0){ continue;} //是目录文件打印为蓝色,不是目录文件分为两种,普通文件是黑色,可执行文件是绿色struct stat filestat;stat(s->d_name,&filestat);if(S_ISDIR(filestat.st_mode)){printf("\033[1;34m%s\033[0m ",s->d_name);}else{if(filestat.st_mode &(S_IXUSR|S_IXGRP|S_IXOTH)){printf("\033[1;32m%s\033[0m ",s->d_name);}elseprintf("%s ",s->d_name);}}printf("\n");closedir(pdir);exit(0);
}