实现cp命令(6)

2010年8月6日 由 edsionte 没有评论 »

Updata:8/10

关于文件复制时的权限问题,更完整和详细的解答见实现cp命令(8)。 此文可能有不正确或者不完整的解释,但是希望你继续阅读,找出问题。

Update:8/09

上次给大家将了my_cp之后,一些同学提出关于文件权限的问题,比如不同用户组之间的文件拷贝涉及到的权限问题。之前对my_cp的测试以及整体设计我都只是针对文件所有者而言,因此这个问题还有待于完善。针对权限问题,我觉得可能还有不少BUG,所以今天就反复测试,结果就发现了如下问题。

我们在进行拷贝文件时,当目的文件不存在时会新建这个目的文件,并进行内容拷贝。目的文件的权限为默认的644。参考下面:

gues@huangwei-desktop:~/code/shell_command$ ls -l test.c
-rw-rw-r-- 1 gues gues 5327 2010-08-09 10:31 test.c
gues@huangwei-desktop:~/code/shell_command$ cp test.c newtest.c
gues@huangwei-desktop:~/code/shell_command$ ls -l test.c newtest.c
-rw-r--r-- 1 gues gues 5327 2010-08-09 10:45 newtest.c
-rw-rw-r-- 1 gues gues 5327 2010-08-09 10:31 test.c

目的文件已经存在时,也会如同上述结果:只拷贝权限,不影响目的文件权限,即保持原目的文件的属性。

那么如何才能拷贝权限?此时就得加入-p选项。目前的my_cp并没有注意到这个问题,只要涉及到拷贝文件,都会将内容和属性一起拷贝。因此我们接下来修改代码:修复my_cp存在的上述漏洞,并且加入-p选项。同时,我们应该稍微修改代码的逻辑判断,为其他选项留条后路。好了,开始吧!

首先在主函数中,我们增加一个对选项-p的判断,注意同时要在声明相应的变量param_p和index_p数组,功能和-r选项相同,具体可参见前文。并且,如果日后要添加其他选项那么直接添加即可。注意,我还增加了一个param全局变量(如果选项不合法,也不碍事),只有命令中含有任何选项,那么它就为真(下面就将用到)。

	//check the legality of the options,only -r or -R
	for(i=1;i<\argc;i++)
	{
		if(argv[i][0]=='-')
		{
			param=1;//have a option
			if((!strcmp(argv[i],"-r")||!strcmp(argv[i],"-R")))
			{
				param_r+=1;
				index_r[i]=i;
			}
			else if(!strcmp(argv[i],"-p"))
			{
				param_p+=1;
				index_p[i]=i;
			}
			/*
			 *if the command include another option,you can add the similar code in here.
			 *You must also add the variable param_?  and index_?[]
			 */
			else
			{
				printf("my_cp:无效选项: %s\n",argv[i]);
				exit(1);
			}
		}
	}

对于获得文件操作数个数和源文件个数的代码也要修改。注意这里的param_r或param_p并不一定为1,当存在相应选项时。因为输入多个相同选项,其效果和一个选项是一样的。

	//get the number of src file
	if(param)
	{
		num=argc-1;//because of "./my_cp"

		if(param_r)
      	        {
		       num-=param_r;
         	}

         	if(param_p)
        	{
			num-=param_p;
        	}

		/*if the command include another option,you can add the similar code in here*/

		src_num=num-1;//because of "dest file"
	}
	else
	{
		//the command doesn't include any option
		num=argc-1;
		src_num=num-1;
	}

另外,当文件数目不合法时,我也对其判断做了优化,具体参见代码。

	if(num<2)
	{
		if(num==0)
		{
			printf("my_cp:缺少了文件操作数\n");
			exit(1);
		}
		if(num==1)
		{
			printf("my_cp:缺少了要操作的目标文件\n");
	        	exit(1);
		}
	}

另外在获取目的文件和源文件的时候,也应该相应添加对-p选项的判断。以上修改使得我们增加其他选型的时候会变得更容易,代码可读性更高。

接下来就是修补上述my_cp的bug了。根据上述举例,只有加入了-p选项时,才会将源文件的属性复制给目的文件,因此我们只要将上述文字翻译成代码即可。

	//set the dset file's access right when the “-p” existed
	if(param_p)
	{
		if(fchmod(fdwt,buf.st_mode)==-1)
        	{
			perror("fchmod");
              		exit(1);
         	}
        	//set file's user id and group id
         	if(fchown(fdwt,buf.st_uid,buf.st_gid)==-1)
         	{
			perror("fchown");
              		exit(1);
         	}
	}

这里还没有完,还记得我们上面讨论分目的文件存在和目的文件不存在两种情况吗?我们还必须修改打开目的文件时open函数的参数。这些参数的的功能:当目的文件不存在时,新建这个文件,文件属性由opne函数第三个参数决定(644);否则,只是清空目的文件中的内容。这些参数刚好满足我们一开始举例后描述的情况。

	//open the dest file
	if((fdwt=open(dest_path,O_CREAT|O_TRUNC|O_RDWR,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))==-1)
	{
		perror("open");
		exit(1);
	}

OK,这下就可以解决上述漏洞了。

实现cp命令(5)

2010年8月4日 由 edsionte 15 条评论 »

晚上7点多给3+1班和linux兴趣小组的同学讲解了my_cp的实现过程。整体还算可以,不过我个人感觉还是有点紧张,个别之处讲的不是很清楚。有时候竟然声音都破音了! e43 看来以后要多锻炼锻炼!不能只会写不会说!我不要做刘永苹(这位老师理论知识充裕,但是表达严重不靠谱)!

1.讲解过程中有一位同学提出这样的问题:当源文件是*.c这样的形式时,my_cp可以正确执行吗。

我当时有点犯傻,的确我在做my_cp的过程中并没有想到这个问题,但是我刚试了以下是可以成功执行的。比如:

gues@huangwei-desktop:~/code/shell_command$ ./my_cp *.c newdir/
gues@huangwei-desktop:~/code/shell_command$ ls -l newdir/
总用量 64
-rw-r--r-- 1 gues gues 7799 2010-08-04 21:06 cp_plus2.c
-rw-r--r-- 1 gues gues 1377 2010-08-04 21:06 e 3_6.c
-rw-r--r-- 1 gues gues    0 2010-08-04 21:06 my_cat.c
-rw-r--r-- 1 gues gues 5327 2010-08-04 21:06 my_chmod.c
-rw-r--r-- 1 gues gues 7881 2010-08-04 21:06 my_cp.c
-rw-r--r-- 1 gues gues 7378 2010-08-04 21:06 my_ls.c
-rw-r--r-- 1 gues gues 6271 2010-08-04 21:06 my_shell.c
-rw-rw-rw- 1 gues gues    0 2010-08-04 21:06 test2.c
-rw-r--r-- 1 gues gues    7 2010-08-04 21:06 test.c
-rw-r--r-- 1 gues gues 7378 2010-08-04 21:06 tfile.c
-rw-r--r-- 1 gues gues 6271 2010-08-04 21:06 ttfile.c

既然可以成功,那么它到底通向my_cp四个功能中的哪一个?从指令形式上看,貌似只有一个源文件:*.c。但是事实上这个指令的功能是将多个文件拷贝到指定目录。最后我通过调试一下就得到了答案!我在主函数一开是就加入了断点:

for(i=1;i<\argc;i++)//断点处
{
//the code here was omited
}

然后我打印argc的值,显示为13!数数上面的.c文件刚好11个,加上./my_cp和dir刚好13个参数。那么答案就是:*.c在输入后,系统自动就查找符合条件的文件,并“暗自“添加到命令中。因此上面的命令等同于:

./my_cp cp_plus2.c my_ls.c test.c   ttfile.c e 3_6.c  my_cat.c  my_chmod.c  my_cp.c  my_shell.c test2.c  tfile.c dir/

2.今天(8/5)woody同学留言说,当出现./my_cp test.c ./././test.c时候,如何处理其错误显示?

这个问题在我的my_cp中没有做到。但是要避免这个问题也不难。在cp_single函数中,当我们判断了目的文件存在时(源文件肯定存在,因为在主函数中已经判断过),可以加入这一段:

		if(stat(src_path,&src_buf)!=0)
		{
			printf("In cp_single:open src_file error.\n");
			return;
		}

		if(src_buf.st_ino==buf.st_ino)
		{
			printf("my_cp: \"%s\"和\"%s\"为同一文件\n",src_path,dest_path);
			return ;
		}

主要思想就是分别获取源和目的文件的属性,再去比较st_ino(文件i节点编号)是否相同。另外还要在cp_directory函数中也要加入类似的代码,因为cp命令是不能将本目录拷贝到本目录下的。

实现chmod命令

2010年8月3日 由 edsionte 1条评论 »

《LinuxC编程实战》一书中在介绍文件操作部分的时候,一开始就给我们举了个实现my_chmod的小程序。它只支持纯数字的权限方式,不支持参数是:权限范围+/-/=权限设置这样的形式。因此我们要实现的my_chmod要能够支持这种形式。具体的功能如下:

my_chmod能够依照权限设置改变文件的权限。权限既可以是代表性的数字,也可以是符号(权限范围+/-/=权限设置)。并且多个符号之间可以用逗号隔开。本文中的my_chmod只支持每个符号中只有一个+/-/=的情况。比如:./my_chmod u+g+o filename 这样的形式本程序并不支持(尽管chmod支持)。

鉴于本博客前文中已经详细讲解了my_cp,因此本文重点说明如何将符号权限设置的方式转化成数字形式。至于符号权限设置的合法性判断等等,无非是利用C语言基础知识来判断,当然这要建立在熟悉chmod命令的基础上。

好了,我们开始吧!

将符号权限转换成数字权限是通过check_option和get_mode两个函数完成的。check_option函数首先判断符号权限的位置合法性。由于多个符号参数可以用逗号隔开,因此在此函数中依次调用get_mode函数,最后将得到最终的mode。比如:./my_chmod u+rw,go+w test.c;那么在check_option函数中分别对两组符号参数(u+rw和go+w)调用get_mode函数。两次调用使得mode为:rw- -w- -w-(622)。但是这时的mode并不是文件最终的mode,还要看文件在执行my_chmod之前的权限,如果test.c 本身权限为644,那么my_chmod执行后,文件权限为666。

当这只是+情况,对于-和=的情况,我们下面会详细说明。

我们再来看get_mode函数的原型:

void get_mode(int u,int g,int o,int r,int w,int x,int sign,mode_t* mode);

这个函数一开始看好像很复杂,因为有众多的参数,其实一点都不复杂。参数u,g,o分别对应用户,用户组,其他成员;而r,w,x当然是可读,可写,可执行;sign根据值1,2,3分别对应的是+,-,=;末尾的mode是将源文件的mode传过来,因为要保存其值,所以传来的是指针。这些变量如果为1那么说明在参数中出现过,否则没有出现。这些变量的赋值在check_option函数中完成。

在get_mode函数中,用一个switch语句来区分+,-,=的不同功能。

	switch(sign)
	{
		case 1://+
			{
				//the code was omited
			}
			break;
		case 2://-
			{
				//the code was omited
			}
			break;
		case 3://=
			{
				//the code was omited
			}
			break;
		default:
			printf("sign error\n");
			exit(1);
	}

首先来看case1,它对应的是+。这里的+比较好理解,就是在文件原来的权限基础上再加上命令中的权限。这里u,g,o三个组分别对应三个if语句,再针对具体一个组,查看这个组具有r,w,x权限的那几个(再分别对应三个if语句)。比如:./my_chmod ug+rwx,o+r test.c;首先他会进入第一个if(u)语句,又因为参数中又出现了rwx,因此if(u)中的三个if语句都会被执行。接着,又会以类似的过程去执行if(g)。这样就完成了一次get_mode的调用,至于o+r,它属于第二次调用get_mode函数。

既然参数中出现了rwx,如何让mode具有对应的rwx权限?如何体现在具体的代码上?我们可以采用或运算,但是注意一定是文件本身的mode分别与相应的权限去或。这里采用了一系列的宏,极大的方便了代码的编写。如果你对此处采用或运算感到困惑,那么不妨手动去试试,即可明白。具体的实现代码如下:

		case 1:
			{
				if(u)
				{
					if(r)
						*mode|=S_IRUSR;
					if(w)
						*mode|=S_IWUSR;
					if(x)
						*mode|=S_IXUSR;
				}
				if(g)
				{
					if(r)
						*mode|=S_IRGRP;
					if(w)
						*mode|=S_IWGRP;
					if(x)
						*mode|=S_IXGRP;
				}
				if(o)
				{
					if(r)
						*mode|=S_IROTH;
					if(w)
						*mode|=S_IWOTH;
					if(x)
						*mode|=S_IXOTH;
				}
			}
			break;

接着看case2,对应的符号是-。它与上面的+刚好相反:在文件原来的权限上减去命令中所输入的权限。这个动作在代码中也很好体现,用异或即可。因为1^1=0,1^0=1,这样使得与相应宏异或的那个权限为0。

但是这里还应该注意一个问题,刚才我们所分析的是当这个权限存在的时候,我们可以用异或来做相应的“减”,如果命令中要减去的权限,文件本身就不具有,那么异或后又会使得文件具有了这个权限!因此我们应该在小if语句的判断处加上一个与运算:判断此时文件的权限是否含有要减去的权限。至于这里为何用异或,与运算,请自己举例,手动运算即可明白。

		case 2:
			{
				if(u)
				{
					if(r&&(*mode&S_IRUSR))
						*mode^=S_IRUSR;
					if(w&&(*mode&S_IWUSR))
						*mode^=S_IWUSR;
					if(x&&(*mode&S_IXUSR))
						*mode^=S_IXUSR;
				}
				if(g)
				{
					if(r&&(*mode&S_IRGRP))
						*mode^=S_IRGRP;
					if(w&&(*mode&S_IWGRP))
						*mode^=S_IWGRP;
					if(x&&(*mode&S_IXGRP))
						*mode^=S_IXGRP;
				}
				if(x)
				{
					if(r&&(*mode&S_IROTH))
						*mode^=S_IROTH;
					if(w&&(*mode&S_IWOTH))
						*mode^=S_IWOTH;
					if(x&&(*mode&S_IXOTH))
						*mode^=S_IXOTH;
				}

			}
			break;

最后是case3的情况。与上述两种情况不同的是,=并不是在文件原有的权限上增加或减少某个权限,而是对命令中出现的某个组的权限进行赋值。也就是说它会覆盖原组中的权限。比如;已知test.c文件的权限为–wx-w-r–(324)。那么./my_chmod ug=r test.c;后,他的权限为:-r–r–r–(444)。可以看到他将ug两组的原权限覆盖,而o组未在命令中出现,因此不受影响。

在进入某个组时候,比如进入U组时,我们会这样处理mode:*mode&=(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);为何要这么做?就如我们刚才分析的那样,我们要为某个组赋值,必须覆盖掉这个组原有权限,因此我们先得到另外两个组所有权限或的结果(在这个或的结果中,另外两个组所有权限对应的那个位均为1),和文件的原权限去与,便可以使得U组权限为0,而其他组的权限不受影响。这其实根据:1&1=1,1&0=0。

		case 3:
			{
				if(u)
				{
					*mode&=(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
					if(r)
						*mode|=S_IRUSR;
					if(w)
						*mode|=S_IWUSR;
					if(x)
						*mode|=S_IXUSR;
				}
				if(g)
				{
					*mode&=(S_IRUSR|S_IWUSR|S_IXUSR|S_IROTH|S_IWOTH|S_IXOTH);
					if(r)
						*mode|=S_IRGRP;
					if(w)
						*mode|=S_IWGRP;
					if(x)
						*mode|=S_IXGRP;
				}
				if(o)
				{
					*mode&=(S_IRGRP|S_IWGRP|S_IXGRP|S_IRUSR|S_IWUSR|S_IXUSR);
					if(r)
						*mode|=S_IROTH;
					if(w)
						*mode|=S_IWOTH;
					if(x)
						*mode|=S_IXOTH;
				}
			}
			break;
		default:
			printf("sign error\n");
			exit(1);

好了,以上便是如何将符号权限转化为数字权限。最终的mode会在check_option函数中得到,这个mode会在chmod函数中用到。

名次确定问题(Update:8/10)

2010年7月29日 由 edsionte 8 条评论 »

五位运动员参加比赛,进行结果预测:

A说:B第一,我第三
B说:我第二,E第四
C说:我第一,D第三
D说:C最后,我第三
E说:我第四,A第一

比赛结束,每位选手只说对了一半,编程确定比赛名次。

拿到这道题,我首先想到的是,ABCDE五人有5!个排列次序,对于每个次序都验证题目要求,满足即可break各个循环。于是代码如下:

#include 

int main()
{
	int i,sum=0;
	int a,b,c,d,e;
	int flag=0;

	for(a=1;a<\6;a++)
	{
		for(b=1;b<\6;b++)
		{
			for(c=1;c<\6;c++)
			{
				for(d=1;d<\6;d++)
				{
					for(e=1;e<\6;e++)
					{

						sum=0;
						sum=(b==1||a==3)+(b==2||e==4)+(c==1||d==2)+(c==5||d==3)+(e==4||a==1);
						if(sum==5)
						{
							flag=1;
							printf("e=%d ",e);
							break;
						}
					}

					if(flag)
					{
						printf("d=%d ",d);
						break;
					}
				}
				if(flag)
				{
					printf("c=%d ",c);
					break;
				}
			}
			if(flag)
			{
				printf("b=%d ",b);
				break;
			}
		}
		if(flag)
		{
			printf("a=%d ",a);
			break;
		}
	}
	printf("\n");
	return 0;
}

编译运行。结果错误:11134。原因是这样的:满足上述条件的序列很多,但是遇到第一个满足条件的序列(此时a,b,c都是1,可见没跑几下就break了)时,因为多个break的原因就跳出了所有循环。看来还得加一个筛选条件。既然名字是1~5的任意排序,那么五位选手名次之和肯定是固定的,因此加入此条件即可筛选处最终名次。如下:

						sum=0;
						sum=(b==1||a==3)+(b==2||e==4)+(c==1||d==2)+(c==5||d==3)+(e==4||a==1);
						if(sum==5)
						{
							if((a+b+c+d+e)==15)
							{
							flag=1;
							printf("e=%d ",e);
							break;
							}
						}

这样就OK了。看来除了得到题目表面的信息后,还得学会抓住隐含条件。其实第一个条件也可以有下面的几种表达方式:

                                                //方法1
						sum=0;
						sum+=(b==1&&a!=3)||(b!=1&&a==3);
						sum+=(b==2&&e!=4)||(b!=2&&e==4);
						sum+=(c==1&&d!=2)||(c!=1&&d==2);
						sum+=(c==5&&d!=3)||(c!=5&&d==3);
						sum+=(e==4&&a!=1)||(e!=4&&a==1);
						//方法2
						sum=0;
						sum=(a==3||a==1)+(b==1||b==2)+(c==1||c==5)+(d==2||d==3)+(e==4);

这里还有一个更简单的方法。在我作完后,我觉得这个题目用5重循环是否太多余,就想看看牛人是如何完成的,所以就咨询牛涛,它发过来一个链接,果然很简便。虽然意思是一样的但是时间复杂度只有N平方而已。我这个N的5次方 e42

Update:8/10

今天一位同学留言说,判断条件应该再加一条判断a,b,c,d,e互不相等。这个条件在我做这个题的时候就想过,可当时总觉的两个判断条件即可。但是,为什么上面的代码也可以显示处正确的答案?那是因为我加入了break。恰好第一组名词就互不相等。如果把每个break都删除了,就会发现有两组结果,其中一个结果就有重复的名次。

其实这个题目属于一个穷举类的题目,应该在程序中让它列出所有的可能(尽管本题中的答案只有一个),而不是找到一个就立刻停止。

实现cp命令(4)

2010年7月28日 由 edsionte 没有评论 »

现在我们已经实现了my_cp。那么我们来运行一下下面的命令吧:

gues@huangwei-desktop:~/code/shell_command$ ./my_cp -r dir/ newdir/ -r
my_cp: can't get file status of "-r" : no this file or directory.

问题出来了,我们并没有考虑到多个重复选项的情况,因此上面命令把末尾的-r当成了文件名。如果用cp执行上面的指令,那么是成功的,因为多个重复选项在cp命令下就相当于一个。因此我们下面来修改代码。
你可以让检查选项处的param_r=1;改为param+=1;然后再加入下面的代码,当出现这种情况的时候,让其出错。

if(param_r>1)
{
printf("my_cp:invalid options.\n");
exit(1);
}

为了完美一些,我们可以这样做。首先我们将原来index_r改成数组index,记录出现-r的位置。我们可以让这个数组全部初始化为0,如果参数中,第i个参数为-r或者-R,那么就将index[i]赋值为i。并且这时候的param_r就要累计出现合法(对于本程序,合法选项就是-r或-R了)选项的个数。

	//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[i]=i;
			}
			else
			{
				printf("my_cp:invalid options: %s\n",argv[i]);
				exit(1);
			}
		}
	}

那么在计算源文件数目的时候也相应的就有了小改动。

	if(param_r)
	{
		num=argc-1-param_r;
		src_num=num-1;
	}
	else
	{
		num=argc-1;
		src_num=num-1;
	}
	if(num<\2)
	{
		printf("my_cp: [option]  \n");
		exit(1);
	}

提取目标文件的时候,就有些小麻烦,但是也是可以解决的。我们从命令行参数末尾开始,找到那个不是选项的那个参数,因为目标文件总是靠近末尾的。比如:./my_cp dir/ newdir/ -r -r

	//extract the dest path
	for(i=argc-1;i>\0;i--)
	{
		if(!strcmp(argv[i],"-r")||!strcmp(argv[i],"-R"))
			continue;
		else
			break;
	}
	if(i==argc-1)
	{
		strcpy(dest_path,argv[i]);
	}
	else
	{
		strcpy(dest_path,argv[i]);
	}

好了,改完上面的代码,下面就和以前的一样了。这样就可以避免开始的时候我们所举的例子的错误。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key