Chinaunix首页 | 论坛 | 博客
  • 博客访问: 56931
  • 博文数量: 22
  • 博客积分: 1546
  • 博客等级: 上尉
  • 技术积分: 230
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-20 20:33
文章分类
文章存档

2010年(22)

分类: C/C++

2010-07-26 11:30:56

今天陈老师对我们以后Linux的学习做了些指导,一针见血的指出我们学习中经常会遇到的问题。我大概记了一下:

1.从有需求做起,不要盲目看书。

2.有计划,有步骤的学习。

3.主动学习,学会分享。

暂不说其他,我觉得第1条在我们学习过程中很值得注意。前段时间做了my_ls.c和my_shell.c。虽然都调试成功,但还是极大的参考了《linuxC编程实战》这本书。因为文件系统在linux中占据着重要地位,所以还是得多通过实践来理解这部分的内容,不妨大家和我一起做my_cp,练习一下文件操作这部分的系统调用函数。

既然我们要实现cp命令,必须得了解cp命令的基本使用方法。这方面的资料网上很多,并且可以参考man手册自己试试,在此我不再详细说明。my_cp将要实现以下基本的功能:

1.将一个文件拷贝到指定路径。(目的文件可存在也可不存在)。这是my_cp中最基本的功能,事实上2,3功能最终都会被分解成此功能。

2.将一个目录拷贝到指定目录下。

3.将一个目录或者多个目录拷贝到指定目录下,此时必须加-R或-r选项。

4.将多个文件拷贝到指定目录下。这里的源文件即包含目录文件也包含普通文件。

个人建议,最好多在终端试试以上各种情况,重点看一看cp命令是如何处理非法命令的,这对我们下面的编程有帮助。

请注意,原文件中如果存在目录文件,并且输入的命令行参数未加-r或-R选项,则会自动忽略此源目录文件,而其他文件的拷贝不受影响。如:

gues@huangwei-desktop:~/code/shell_command$ ls
ls  ls1  my_cp  my_cp1  my_cp.c  my_ls.c  my_shell.c  newls.c  t.c  tdir  test  tfile.c  ttfile.c
gues@huangwei-desktop:~/code/shell_command$ cp my_shell.c tdir/ test/
cp: 略过目录"tdir/"
gues@huangwei-desktop:~/code/shell_command$ ls test/
my_shell.c

如果你完成了my_ls或者my_shell,你一定会知道,这类实现系统命令的程序在main函数中首先会对命令行参数进行解析,判断其合法性,然后再根据用户的输入(是否包含某选项或者参数个数等)来“引导”程序进入其他子函数,以便完成相应功能。想想my_ls程序,难道不是这样吗?

好了,我们开始吧!

首先进入主函数。由于本程序只支持-r或者-R选项,因此如果用户输入选项,我们来判断是否合法。如果合法让其标志param_r为真。由于-r或-R选项位置不一定限制在cp命令之后(事实上放在命令行参数末尾也可以),所以用index_r来保存其下标。实现代码如下:(因为插件原因,下面的代码在出现<或>符号的后面均加入了 \ 

	char dest_path[PATH_MAX+1];
	char src_path[256][PATH_MAX+1];
	int i,k,num,src_num,index_r;
	struct stat buf;

	//check the legality of the options,only -r or -R
	for(i=1;i<\argc;i++)
	{
		if(argv[i][0]=='-')
		{
			if((!strcmp(argv[i],"-r")||!strcmp(argv[i],"-R")))
			{
				param_r=1;
				index_r=i;
			}
			else
			{
				printf("my_cp:invalid options: %s\n",argv[i]);
				exit(1);
			}
		}
	}

接下来计算命令行参数中参数的个数num以及源文件的个数src_num,这两个变量备用。如果num小于2,肯定不合法。上述要求都合法后,提取目标文件的路径,上面已经说过因为R和r选项后可以出现在参数末尾,因此得多加一次判断,并不能直接认为argv[argc-1]就是目标文件路径。

if(param_r)
	{
		num=argc-1-1;
		src_num=num-1;
	}
	else
	{
		num=argc-1;
		src_num=num-1;
	}
	if(num<\2)
	{
		printf("my_cp: [option] (source) (dest)\n");
		exit(1);
	}
	//extract the dest path
	if(index_r!=argc-1)
	{
		strcpy(dest_path,argv[argc-1]);
	}
	else
	{
		strcpy(dest_path,argv[argc-2]);
	}

接下来提取源文件的路径,由于源文件可以有多个,因此我们用一个字符串数组来存储源文件路径。

	//extract the src path
	k=0;
	for(i=1;i<\argc-1;i++)
	{
		if(i==index_r&¶m_r)
			continue;
		else
		{
			strcpy(src_path[k++],argv[i]);
		}
	}

以上工作都做好后,我们可以"分流“了,即根据不同要求进入不同的子函数。我的“分流”原则是根据源文件数src_num。当其大于1时,说明源文件是多个。首先判断此目的文件是否存在,如果存在那么接着判断它是否为一个目录文件,因为多个源文件不可能拷贝到一个非目录文件当中。
确定了目的文件是一个目录后,我们要将这个目录下的所有文件依次调用子函数进行“分流”。
这里有两个重要的子函数,cp_single函数针对这样的情况:将单个文件拷贝到另一文件。(功能1)。cp_directory函数针对这样的情况:将单个文件夹拷贝到指定目录(功能2)。进入这两个子函数的依据就是,如果源文件是一个文件进入前者,源文件是目录进入后者。而我们用for循环将上述两函数有效的结合起来,就可以实现上述功能的3,4。

	if(src_num>\1)
	{
		if(stat(dest_path,&buf)==-1)
		{
			printf("my_cp: \"%s\" is not a directory.\n",dest_path);
			exit(1);
		}
		//the dest path is valid
		if(S_ISDIR(buf.st_mode))
		{
			strcpy(temp_dest_path,dest_path);
			//the dest path is directory
			for(i=0;i<\src_num;i++)
			{
				if(stat(src_path[i],&buf)==-1)
				{
					printf("my_cp: can't get file status of \"%s\" : no this file or directory.\n",src_path[i]);
					continue;
				}
				//the src_path exist
				if(!S_ISDIR(buf.st_mode))
				{
					cp_single(src_path[i],dest_path);
				}
				else if(param_r)
				{
					cp_directory(src_path[i],dest_path);

				}
				else
				{
					printf("my_cp: skip the directory: \"%s\".\n",src_path[i]);
				}
				strcpy(dest_path,temp_dest_path);

			}

		}
		else
		{
			printf("my_cp: \"%s\" is not a directory.\n",dest_path);
			exit(1);
		}
	}
	else
	{
		//The code here be omited
	}

我们接下来主要来看源文件数目为1的情况。其实这属于cp命令最基本的功能,cp_directory函数也会调用这个函数。
源文件存在的时候,如果源文件是一个目录并且有r或R选项,那么进入cp_directory函数(至于目标文件是否为目录,进入此函数可以判断)。如果源文件不是目录文件,那么进入cp_single函数即可。

	if(src_num>\1)
	{
		//The code here be omited
	}
	else
	{
		//only one src path
		if(stat(src_path[0],&buf)==-1)
		{
			printf("my_cp: can't get file status of \"%s\" : no this file or directory.\n",src_path[0]);
			exit(1);
		}
		if(S_ISDIR(buf.st_mode))
		{
			if(param_r)
			{
				cp_directory(src_path[0],dest_path);
				exit(0);
			}
			else
			{
				printf("my_cp: skip the directory: \"%s\".\n",src_path[0]);
				exit(1);
			}
		}
		else
                {
			cp_single(src_path[0],dest_path);
		}
	}

好了,主函数部分基本就是这样,这里主要说明整个主函数的整个流程。另外我们也应该不走寻常路,去尝试一些错误情况,这有利于我们编程。

阅读(1207) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~