目录
2.获取与当前用户的相关信息---一个结构体(passwd)
一、Mybash介绍
1.1.mybash.c
这个函数用来实现mybash,可分为三大部分:打印函数、分割函数、命令函数
打印函数
打印出如下的功能
能够和bash一样显示出用户+主机名+当前目录+用户身份
分割函数
将输入的命令按空格分割,并保存到相应的数组中,使用到strtok()字符分割函数。
命令函数
根据exec系列学习,采用fork+exec,父进程等待,子进程采用exec,调用自己实现的命令文件,进行真实命令的模拟
二、Mybash实现
2.1.打印函数
2.1.1需要使用到的功能函数
1.获取与当前用户关联的UID
uid_t getuid(void);
如果得到是0,就是管理员,初始为普通用户。
2.获取与当前用户的相关信息---一个结构体(passwd)
struct passwd*getwuid(uid_t uid);
这里可以定义一个相对应类型的指针ptr去指向该结构体,进而->方式访问到该用户的UID号
3.获取主机信息
int gethostname(char*name,size_t namelen);
gethostname函数把机器的主机名写入到name字符串里,该字符串至少有namelen个字符长。而成功,gethostname的返回0,否则返回-1。
4.获取当前所处位置
char*getcwd(char*buf,size_t size);
getcwd函数把当前目录的名字写入给定的缓冲区-buf,如果目录名长度超出参数size给出的缓冲区长度,就返回NULL,如果成功,返回指针buf。
5.给对应字体加颜色
详细参考printf函数中的/033一系列。
2.1.2 打印函数实现
void print_info()
{
uid_t uid=getuid();
char*s="$";
if(uid==0)
{
s="#";
}
struct passwd* ptr=getpwuid(uid);
if(ptr==NULL)
{
printf("mybash 1.0 $");
fflush(stdout);
return;
}
char hostname[128]={0};
if(gethostname(hostname,128)==-1)
{
printf("mybash 1.0 $");
fflush(stdout);
return;
}
char curr_dir[256]={0};
if(getcwd(curr_dir,256)==NULL)
{
printf("mybash 1.0 $");
fflush(stdout);
return;
}
printf("\033[1;32m%s@%s\033[0m\033[1;34m:%s\033[0m %s ",ptr->pw_name,hostname,curr_dir,s);
fflush(stdout);
return;
}
2.2 分割函数
2.2.1 strtok函数
char* strtok(char*str,const char*delim);
str为需要分割的字符串,delim为分割标记。
通常需要循环使用,如果需要多次分割当前字符串,在第二次使用的时候字符串那使用NULL,系统会认为继续对当前字符串进行分割(这里有个指针会指向第一次分割完后的位置)。
2.2.2分割函数实现
char* get_cmd(char buff[],char*myargv[])
{
if(buff==NULL||myargv ==NULL)
{
return NULL;
}
int i=0;
char*s=strtok(buff," ");
while(s!=NULL)
{
myargv[i++]=s;
s=strtok(NULL," ");
}
return myargv[0];
}
2.3 命令函数
2.3.1 exit
exit表示退出当前进程。
if(strcmp(cmd,exit)==0)
{
break;
}
2.3.2 clear
clear表示清屏,并将光标放置在最上方最左边位置
\033[2J 清屏
\033[y;xH设置光标位置
if(strcmp(cmd,"clear")==0)
{
printf("/033[2J]\033[0;0H");
}
2.3.3 cd
首先先说下myargv就是argv,在前面的文里说过int main(int argc,char*argv[],char*envp[]),这里的myargv里面就存储了需要切换到的路径。
其次,程序可以像用户在文件系统里那样来切换目录,就像在shell里面使用cd命令来切换目录一样。程序使用的是chdir系统调用。
int chdir(const char*path);
这里的path存储在myargv[1]里面,myargv[0]为命令cd .
成功返回0,失败返回-1
if(strcmp(cmd,"cd")==0)
{
if(myargv[1]!=NULL)
{
if(chdir(myargv[1])==-1)
{
perror("cd err\n");
}
}
continue;
}
2.3.4 chmod
首先先来看看shell里面的chmod
第一个参数为权限设置,第二个参数为需要修改的文件
所以我们通过查看书,可以发现chmod的系统调用
int chmod(const char*path,mode_t mode);
path参数指定的文件被修改为具有mode参数给出的访问权限。
注意我们这里的参数为8进制,所以涉及到参数进制转换。
if(strcmp(cmd,"chmod")==0)
{
const char*path=myargv[2];
int mode=atoi(myargv[1]);
int mode_1=mode/10/10;
int mode_3=mode%10;
int mode_2=mode/10%10;
mode=mode_1*8*8+mode_2*8+mode_3;
if(chmod(path,mode)<0)
{
printf("chmod err\n");
}
continue;
}
2.3.5 pwd
getcwd在上面的打印函数中提过,详细请参考上面。
if(strcmp(cmd,"pwd")==0)
{
char cwd[128]={0};
if(getcwd(cwd,128)==NULL)
{
printf("pwd err\n");
continue;
}
printf("%s\n",cwd);
continue;
}
2.3.6 mkdir
mkdir系统调用用于创建目录,他相当于mkdir程序。mkdir调用将参数path作为新建目录的名字。目录的权限由参数mode设定,将含义按open系统调用的O_CREAT选项的有关定义设置。
int mkdir(const char* path,mode_t mode);
注意mode数值的设定。
if(strcmp(cmd,"mkdir")==0)
{
char* path=myargv[1];
mode_t mode=7*8*8+7*8+5;
if(mkdir(path,mode)==-1)
{
printf("mkdir err\n");
}
continue;
}
2.3.7 rmdir
rmdir系统调用用于删除目录,但只有在目录为空才行,rmdir程序就是用这个系统调用来完成工作的。
int rmdir(const char*path);
if(strcmp(cmd,"rmdir")==0)
{
char*path=myargv[1];
DIR* dir = opendir("path");
if(dir == NULL){
perror("打开目录失败");
exit(1);
}
struct dirent *ptr = NULL;
while((ptr=readdir(dir))!=NULL);
if(ptr == NULL){
rmdir(myargv[1]);
}
continue;
}
这里我先通过调用opendir和readdir两个函数来判断当前目录是否为空,进而使用rmdir函数来删除当前目录。
opendir函数的作用是打开一个目录并建立一个目录流,成功返回一个指向DIR结构的指针,该指针用于读取目录数据项,失败则返回一个空指针。
DIR*opendir(const char*name);
然后继续使用readdir()函数,返回一个指针,该指针指向结构里面保存着目录流dirp中下一个目录项的有关资源,后续的readdir调用返回后续的目录项,如果错误或者到达目录尾则返回NULL。
因此使用了while循环。
2.3.8 rm
rm可以直接删除普通目录文件,但是不能直接删除普通文件,需要使用到rm -r
因此这里我们需要获取到当前文件的类型
文件的类型存储在struct stat
if(strcmp(cmd,"rm")==0)
{
char*path1=myargv[1];
char*path2=myargv[2];
struct stat buf;
char * ptr;
int result;
result = stat(myargv[3], &buf);
if(S_ISREG(buf.st_mode))
{
if(strcmp(cmd,strcat(path1,path2))==0)
{
unlink(myargv[3]);
}
}
if(S_ISDIR(buf.st_mode))
{
rmdir(myargv[3]);
}
}
如果是普通文件,我们使用unlink系统调用删除一个文件。
unlink系统删除一个文件的目录项并减少他的链接数,它成功返回0失败返回-1.
如果想通过这个函数成功删除文件,你就必须拥有该文件所属目录的写和执行权限。
2.3.9 复制、更新进程
pid_t pid=fork();
if(pid==-1)
{
printf("fork err\n");
continue;
}
if(pid==0)
{
execvp(cmd,myargv);
printf("exec err\n");
exit(0);
}
wait(NULL);
剩余命令我们后续更新。。。。敬请期待
接下来我们看一下效果
三、使用看效果
下次我们更新touch ,mv,cp,ls,cat等命令