#include<stdlib.h>
#include<unistd.h>
#include<dirent.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<sys/stat.h>
#define M 100
#define N 256
void printInfo(); /*打印提示信息*/
void com_input(char *); /*得到输入的命令字符串*/
void analyze_com(char *,int *,char a[][N]); /*解析命令行*/
void do_com(int ,char [][N]); /*根据相关参数执行命令*/
int find_com(char *);
/*1、主函数入口*/
int main(int argc,char **argv)
{
int i=0;
int j=0;
int k=0;
char comlist[3][N]; /*包含命令和参数在内的字符串数组内容*/
char *buf=NULL; /*用来记录从屏幕中输入的字符串的内容*/
char *p;
buf=(char *)malloc(sizeof(char)*N);
if(buf==NULL)
{
perror("malloc failed.\n");
exit(0);
}
while(1)
{
i=0;
j=0;
k=0;
printInfo();
printf("$");
while(i<N)
{
buf[i++]='\0'; /*清空buf中的内容*/
comlist[0][i]='\0';
comlist[1][i]='\0';
comlist[2][i]='\0';
}
com_input(buf);
if(strcmp(buf,"exit\n")==0||strcmp(buf,"quit\n")==0)
break;
for(i=0;i<strlen(buf)-1;i++)
{
if(buf[i]!=' ')
comlist[j][k++]=buf[i];
else
{
p=buf+i+1;
comlist[j][k]='\0';
j++;
k=0;
}
}
if(strcmp(comlist[0],"cat")==0)
{
do_cat(comlist[1]);
}
else if(strcmp(comlist[0],"touch")==0)
{
do_touch(comlist[1]);
}
else if(strcmp(comlist[0],"cp")==0)
{
do_cp(comlist[1],comlist[2]);
}
else if(strcmp(comlist[0],"cd")==0)
{
do_cd(comlist[1]);
}
else
{
printf("not the cd command,please input again...\n");
}
}
if(buf!=NULL)
{
free(buf);
buf=NULL; /*使buf重新指向NULL*/
}
exit(0);
}
/*2、界面提示函数*/
void printInfo()
{
printf("chi's shell:~");
}
/*3、得到用户输入的内容*/
void com_input(char *buf)
{
int len=0;
char ch;
ch=getchar();
while(len<N&&ch!='\n')
{
buf[len++]=ch;
ch=getchar();
}
if(len>=N)
{
printf("the command that you input is too long!\n");
exit(-1);
}
buf[len]='\n';
}
/*4.执行cd命令*/
void do_cd(char arg[N])
{
long cur_path_len;
char *cur_dir;
if((cur_dir=(char *)malloc(sizeof(char)*N))==NULL)
{
perror("not create memory space for the cur_dir");
return ;
}
if((cur_path_len=pathconf(".",_PC_PATH_MAX))==-1)
{
perror("Count Create the MAX path in your workbench\n");
return ;
}
if(chdir(arg)==-1)
{
perror("not change your workbench directory!\n");
return ;
}
else
{
if(getcwd(cur_dir,cur_path_len)==NULL)
{
perror("Couldn't get current directory\n");
return ;
}
else
{
printInfo();
printf("%s$",cur_dir);
}
}
}
/*5.实现copy命令*/
void do_cp(char src[N],char dest[N])
{
char buf[100];/*读取文件的内容保存在buf中*/
int num;/*记录每一次存入buf中的字符数*/
int fd1,fd2;/*用于保存打开文件的文件描述符*/
if((strlen(src)==0)||(strlen(dest)==0))
{
printf("you don't input the source file name or destination file name!\n");
return;
}
if((fd1=open(src,O_RDONLY))==-1){
perror("Can't open the source file!\n");
return;
}
if((fd2=open(dest,O_CREAT|O_WRONLY,0777))==-1)
{
perror("Can't creat the destination file!\n");
return;
}
while((num=read(fd1,buf,100))>0)
{
if(write(fd2,buf,num)==-1)
{
perror("Con't write the file content to the file!\n");
return;
}
}
printf("OK! the file had been wrote to the destination file\n ");
close(fd1);
close(fd2);
}
/*6、实现touch命令*/
void do_touch(char arg[N])
{
int fd;
char buf[100];
int num;
/*第一种方法 if((fd=open(arg,O_CREAT|O_EXCL,S_IRUSR|S_IWUSR))==-1){*/
/*第二种*/ if((fd=creat(arg,S_IRWXU))==-1){
perror("open");
exit(-1);
}
else
printf("create file success!\n");
close(fd);
}
/*7、实现cat命令*/
void do_cat(char arg[N])
{
char buf[100];/*读取文件的内容保存在buf中*/
int num;/*记录每一次存入buf中的字符数*/
int fd;/*用于保存打开文件的文件描述符*/
if(strlen(arg)==0)
{
printf("you don't input the file name!\n");
return;
}
if((fd=open(arg,O_RDONLY))==-1){
perror("Can't open the file!\n");
return;
}
while((num=read(fd,buf,99))>0)
{
buf[num]='\0';
printf("%s",buf);
}
close(fd);
}
此次的程序完成了对文件的打开和写入以及创建文件在完成cat 命令后,完成对cp 、touch 两个命令的解析和执行。对linux 系统中文件的I/O 操作方式,与标准C 语言下的文件的打开与读写进行了对比。在上次对目录的操作基础上加深对linux 文件系统的管理。这次完成了对cat 、cp 、touch 三个命令的解析和应用。用到了open 、read 、write 、creat 函数。它们所需头文件#include <sys/types.h> ,#include <sys/stat.h> ,#include <fcntl.h> 函数原型分别为:
int open(const char *pathname, int flags); 用flags 指定的操作打开pathname 指定的文件。成功返回一个文件描述符,失败返回-1 并设置errno 。常用的flags 包括O_RDONLY, O_WRONLY, O_RDWR,O_CREAT, O_TRUNC, O_APPEND 等等。它们直接可以在合理的前提下进行逻辑或,例如O_CREAT | O_TRUNC | O_WRONLY 。
ssize_t read(int fd, const void *buf, size_t count); 作用:从描述符fd 引用的文件中读出count 个字节到buf 所指向的缓冲区。成功返回实际读出的字节数,失败返回-1 并设置errno 。
ssize_t write(int fd, const void *buf, size_t count);
作用:把buf 指向的缓冲区中的count 个字节写入到描述符fd 引用的文件。成功返回实际写入的字节数,失败返回-1 并设置errno 。
(III)调试过程
调试中修改了上次的一些问题,改正了对于实现cd命令时输入语句只能解析为两个参数(含命令在内)的限制,扩充了三个参数的命令。调试中关键的实现cp命令式不能写入文件,却写到屏幕上,但这时文件却已经产生,只是没有内容。后来不断测试和断点打印还是一样的结果,最后在和同学的讨论下发现自己在要写入文件的打开语句时写成了if((fd2=open(dest,O_CREAT|O_WRONLY,0777)==-1)),经分析发现fd2最终的值是0,因为先进行==判断不成立,结果为0,便赋予fd2。因此输入到了屏幕,但是还是不解在linux中有STDIN,STDOUT,STDERR,即:0,1,2表示,即fd2为标准输入,怎么会输入到屏幕呀,而且即便也是输入到屏幕也应该是1.在网上查了相应文章都是一样的。所以这一点还是不解问什么会输入到屏幕中。最终改为if((fd2=open(dest,O_CREAT|O_WRONLY,0777))==-1)程序便运行正确。至此,相应的命令得到正确解析和完成
(IV)实验收获
在更改输入命令解析时发现对于指针的操作来截断命令和得到参数上还是存在理解不透彻,看着程序认为修改后没有很么问题,可是运行后问题就还是存在一些,没有考虑周全。在长时间的调试中才逐渐发现了错误之处。程序中的一些细微之处的错误也是导致时间的大量浪费,而且每一次的编程中都不能避免,如字符串指针变量循环使用时忘记清空内容,再输入结果中出现后序的字符。这次对文件的操作也是叫难的,理解linux下的文件访问机制和修改机制是不同于windows下的文件操作。