一、使用系统命令的完整代码
#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," ");//第一次调用strtok
int 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 + exec
run_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," ");//第一次调用strtok
int 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 + exec
run_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);
}
else
printf("%s ",s->d_name);
}
}
printf("\n");
closedir(pdir);
exit(0);
}