2

《Unix/Linux编程实践教程》笔记(2)

 2 years ago
source link: https://houye.xyz/2017-11/uup2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

man 3 readdir 可得到答案,大意是因为posix定义d_name为 char d_name[]数组。

usermode,useradd都无法使用户的ID相同,可以先新建两个用户,再修改etc/passwd 和/etc/group中的内容。实践证明,系统是以ID判别用户的,相同ID即为相同用户。

对于目录来说,执行权限就是切换和搜索目录的权限。关掉目录执行权限的话,可以查看目录里的内容,但是无法切换到此目录和搜索目录内容。

login源码在util-linux包中,修改终端设备文件所有者的代码是chown_tty。注销时,终端设备文件所有者改回成root的程序暂时不知道。

3.10 ls分列输出

man 4 tty_ioctl可得到获取终端大小的信息。如下代码实现了ls的分列输出。

#include	<stdio.h>
#include	<stdlib.h>
#include	<sys/types.h>
#include	<dirent.h>
#include	<termios.h>
#include	<sys/ioctl.h>
#include	<unistd.h>
#include	<string.h>

char filename[128][128];//存储文件名
int lenoffilename[128];//存储文件名长度
int max[128];//存储每列文件名的最大长度

int getcol( int tmpnumfile ,int tmpwscol )
{
	int col = 0; //最终输出的列数
	int sum = 0; //存储文件明长度之合
	int k = 0;
	int i = 0;

	//得到初始的列数
	for (i = 0; i < tmpnumfile; i++)
	{
		max[i] = lenoffilename[i];
		sum += lenoffilename[i]+2;
		if (sum >= tmpwscol)
		{
			col = i;
			max[i] = 0;
			break;
		}
	}
	//所有文件不足一行,返回col
	if (i >= tmpnumfile)
	{
		col = i;
		return col;
	}
	
	memset(max, 0, 128*sizeof(int)); //数组置0
	for (i = 0; i < col; i++)
	{
		//取得这一列的最大文件长度,存储到max数组
		for (int j = 0; ; j++)
		{
			if ( (i+j*col) <= tmpnumfile )
			{
				if (lenoffilename[i+j*col] > max[i])
					max[i] = lenoffilename[i+j*col];
			}
			else
				break;
		}
		
		/*判断每列最大文件名长度之和是否超出终端宽度
		 *如果是,则col减1, i设为-1(for循环i++,i就变成0),max数组置0, 跳出for k循环
		 *如果否,不做处理,继续
		 */
		sum = 0;
		for (k = 0; k < col; k++)
		{
			sum += max[k]+2;
			if (sum >=tmpwscol)
			{
				col--;
				i = -1;
				memset(max, 0, 128*sizeof(int));
				break;

			}
		}
	}

	return col;
}

/* 得到文件名,存储到filename数组
 * 得到文件名长度,存储到lenoffilename数组
 * 返回文件数目
 */
int getfilename(char *dirname )
{
	DIR		*dir_ptr;		/* the directory */
	struct dirent	*direntp;		/* each entry	 */
	int numfile = 0;
	if ( ( dir_ptr = opendir( dirname ) ) == NULL )
		fprintf(stderr,"ls1: cannot open %s\n", dirname);
	else
	{
		while ( ( direntp = readdir( dir_ptr ) ) != NULL )
		{
			//printf("%s\n", direntp->d_name );
			strcpy(filename[numfile], direntp->d_name);
			lenoffilename[numfile++] = strlen(direntp->d_name);
		}
		closedir(dir_ptr);
	}

	return numfile;

}
int main(int ac, char *av[])
{
	struct winsize size;
	int tmpnumfile, col;
	if (isatty(STDOUT_FILENO) == 0)
		exit;
	//得到终端的size
	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) < 0)
	{
		perror("ioctl TIOCGWINSZ error");
		exit(1);
	}

	if ( ac == 1 )
	{
		tmpnumfile = getfilename( "." );
		col = getcol( tmpnumfile, size.ws_col);
		do_ls(col, tmpnumfile);
	}
	else
		while ( --ac )
		{
			printf("%s:\n", *++av );
			tmpnumfile = getfilename( *av );
			col = getcol( tmpnumfile, size.ws_col );
			do_ls(col, tmpnumfile);
		}

	return 0;
}

void do_ls( int col, int tmpnumfile)
{
	for (int i = 0; i < tmpnumfile; i++)
	{
		printf ("%s  ", filename[i]);

		//输出空格补全
		for( int j = 0; j < (max[i%col]-lenoffilename[i]); j++)
			printf (" ");
		if ( (i+1)%col == 0)
			printf ("\n");

	}
	printf ("\n");
	return;
}

3.11 使ls接收目录参数

do_ls中传给dostat()的只是一个文件名,没有将路径一并传进去。为了让ls2能接收其他目录的作为参数,所以修改了do_ls函数,拼接好一个完整的文件路径,传给dostat函数。do_ls函数代码如下:

do_ls( char dirname[] )
/*
 *	list files in directory called dirname
 */
{
	DIR		*dir_ptr;		/* the directory */
	struct dirent	*direntp;		/* each entry	 */
	char *tmpdirname;
	int len = strlen(dirname);
	char *sep = "/";

	if ( ( dir_ptr = opendir( dirname ) ) == NULL )
		fprintf(stderr,"ls2: cannot open %s\n", dirname);
	else
	{
		while ( ( direntp = readdir( dir_ptr ) ) != NULL )
		{
			tmpdirname = (char *)malloc(strlen(direntp->d_name)+len+2);
			memset(tmpdirname, 0, strlen(direntp->d_name)+len+2);
			
			strcat (tmpdirname, dirname);

			if (tmpdirname[len-1] != '/')
				strcat(tmpdirname, sep);
			
			strcat(tmpdirname, direntp->d_name);
			dostat(tmpdirname);
			free(tmpdirname);
			tmpdirname = NULL;
		}
		closedir(dir_ptr);
	}
}

3.12 ls显示特殊权限

man 2 stat可以得到特殊权限的掩码,输出显示时还需要注意,如果文件或者目录没有对应的x权限,对应位置上的s或者t应是大写的S或者T。实现代码如下:

permbits( permval, specpos, bit0, string )
char *string;
char bit0;
/*
 *	convert bits in permval into chars rw and x
 */
{
	//printf ("bit0 is%c\n", bit0);
	
	if ( permval & 4 ) //这里是xxx & 0000000100
		string[0] = 'r';
	if ( permval & 2 )
		string[1] = 'w';
	if ( permval & 1 )
	{
		if (specpos & 1)
		{
			if (bit0 == 'd')
				string[2] = 't';
			else
				string[2] = 's';
		}
		string[2] = 'x';
	}
	else
	{
		if (specpos & 1)
		{
			if (bit0 == 'd')
				string[2] = 'T';
			else
				string[2] = 'S';
		}
	}
}

3.13 使cp支持第二个参数是目录

代码如下:

main(int ac, char *av[])
{
        int     in_fd, out_fd, n_chars, len;
        char    buf[BUFFERSIZE];
		struct stat info;
		char *pathtofile;
		char *sep = "/";
						/* check args 	*/
        if ( ac != 3 ){
                fprintf( stderr, "usage: %s source destination\n", *av);
                exit(1);
        }
						/* open files	*/

        if ( (in_fd=open(av[1], O_RDONLY)) == -1 )
                oops("Cannot open ", av[1]);

		//加判断,判断第二个参数是否是目录

		if (stat(av[2], &info) == -1)
			perror( av[2]);
		if ( (info.st_mode & S_IFMT) == S_IFDIR )
		{
			len = strlen(av[2]);
			pathtofile =  (char *)malloc(strlen(av[1])+len+2);
			memset(pathtofile, 0, strlen(av[1]+len+2));

			strcat(pathtofile, av[2]);
			if (pathtofile[len-1] != '/')
				strcat(pathtofile, sep);

			strcat(pathtofile, av[1]);

			if ( (out_fd=creat( pathtofile, COPYMODE)) == -1 )
                		oops( "Cannot creat", av[2]);
			free(pathtofile);
			pathtofile = NULL;
				
		}
		else 
		{
			if ( (out_fd=creat( av[2], COPYMODE)) == -1 )
                oops( "Cannot creat", av[2]);
		}
	
						/* copy files	*/

        while ( (n_chars = read(in_fd , buf, BUFFERSIZE)) > 0 )
                if ( write( out_fd, buf, n_chars ) != n_chars )
                        oops("Write error to ", av[2]);
	if ( n_chars == -1 )
			oops("Read error from ", av[1]);

						/* close files	*/

        if ( close(in_fd) == -1 || close(out_fd) == -1 )
                oops("Error closing files","");
}

3.14 使cp支持第一个参数使目录

修改cp1.c,使得可以将第一个目录下的所有文件拷贝到第二个目录下

include        <stdio.h>
#include        <unistd.h>
#include        <fcntl.h>
#include		<sys/stat.h>
#include		<string.h>
#include		<stdlib.h>
#include		<dirent.h>

#define BUFFERSIZE      4096
#define COPYMODE        0644

void oops(char *, char *);
void cpfile(const char *pf, const char *todir);
char *combpath(const char *dir, const char *pf);

char *sep = "/";
char    buf[BUFFERSIZE];
main(int ac, char *av[])
{
        int     in_fd, out_fd, n_chars, len;
		char *pathtofile;
		struct stat info1, info2;
		DIR		*dir_ptr;
		struct dirent *direntp;
						/* check args 	*/
        if ( ac != 3 ){
                fprintf( stderr, "usage: %s source destination\n", *av);
                exit(1);
        }
						/* open files	*/
		/*
        if ( (in_fd=open(av[1], O_RDONLY)) == -1 )
                oops("Cannot open ", av[1]);
		*/

		//加判断,判断第二个参数是否是目录
		
		if (stat(av[2], &info2) == -1)
			perror( av[2]);
		if ( (info2.st_mode & S_IFMT) == S_IFDIR )
		{
			//如果第一个参数是目录,将这个目录下所有文件拷贝到第二个参数的目录下
			if (stat(av[1], &info1) == -1)
				perror( av[1]);
			if ((info1.st_mode & S_IFMT) == S_IFDIR )
			{
				//循环读取av[1]目录下文件,写到新的目录下
				if (( dir_ptr = opendir( av[1] )) == NULL)
					fprintf (stderr, "opendir: cannot open %s\n", av[1]);
				char *tmppathtofile = NULL;
				while ( ( direntp = readdir( dir_ptr )) != NULL)
				{
					printf (" in %s is %s\n", av[1], direntp->d_name);
					if (strcmp(direntp->d_name, ".") == 0 || strcmp(direntp->d_name, "..") == 0)
						continue;

					pathtofile = combpath(av[1], direntp->d_name);
					tmppathtofile = combpath(av[2], direntp->d_name);
					
					cpfile(pathtofile, tmppathtofile);

					free(pathtofile);
					free(tmppathtofile);
					pathtofile = NULL;
					tmppathtofile = NULL;
				}
				return;
			}
			else
			{
				//如果av[1]是文件,av[2]是目录,拼接完整文件路径
				pathtofile = combpath(av[2], av[1]);
			}
				
				
		}
		else 
		{
			//如果av[2]是文件
			cpfile(av[1], av[2]);
			return;
		}
		//av[1]是文件,av[2]是目录,将av[1]拷贝到av[2]目录下
		cpfile(av[1], pathtofile);
		free(pathtofile);
}

void oops(char *s1, char *s2)
{
        fprintf(stderr,"Error: %s ", s1);
        perror(s2);
        exit(1);
}

void cpfile(const char *inpf, const char *outpf)
{
	int out_fd, in_fd, n_chars;
	if ( (out_fd=creat( outpf, COPYMODE)) == -1 )
		oops( "Cannot creat", outpf);

	if ( (in_fd=open( inpf, COPYMODE)) == -1 )
		oops( "Cannot open", inpf);
	//printf ("%s \n", inpf);

	while ( (n_chars = read(in_fd , buf, BUFFERSIZE)) > 0 )
			if ( write( out_fd, buf, n_chars ) != n_chars )
					oops("Write error to ", outpf);
	if ( n_chars == -1 )
		oops("Read error from ", inpf);

	if ( close(in_fd) == -1 || close(out_fd) == -1 )
			oops("Error closing files","");

}
char * combpath(const char *dir, const char *pf)
{
	int len;
	len = strlen(dir);
	char *pathtofile =  (char *)malloc(strlen(pf)+len+2);
	memset(pathtofile, 0, strlen(pf)+len+2);

	strcat(pathtofile, dir);
	if (pathtofile[len-1] != '/')
		strcat(pathtofile, sep);

	strcat(pathtofile, pf);

	return pathtofile;

}

3.15 ls根据文件名排序输出

把文件名储存在数组里,再用快排排序。

#include	<stdio.h>
#include	<sys/types.h>
#include	<dirent.h>
#include	<string.h>
#include	<stdlib.h>
char dirlist[128][128];


int cmp(const void *a, const void *b);
main(int ac, char *av[])
{
	if ( ac == 1 )
		do_ls( "." );
	else
		while ( --ac ){
			printf("%s:\n", *++av );
			do_ls( *av );
		}
}

do_ls( char *dirname )
{
	DIR		*dir_ptr;		/* the directory */
	struct dirent	*direntp;		/* each entry	 */

	if ( ( dir_ptr = opendir( dirname ) ) == NULL )
		fprintf(stderr,"ls1: cannot open %s\n", dirname);
	else
	{
		int i = 0;
		while ( ( direntp = readdir( dir_ptr ) ) != NULL )
		{
			strcpy(dirlist[i++], direntp->d_name);
		}
		printf ("sizeof () is %d\n", sizeof(dirlist[0]));
		qsort(dirlist, i, sizeof(dirlist[0]), cmp);
		for (int j = 0; j<i; j++)
			printf ("%s\n", dirlist[j]);
		closedir(dir_ptr);
	}
}
int cmp(const void *ap, const void *bp)
{
	return strcmp((char *)ap, (char *)bp);
}

3.17 权限检查在open进行

用vim打开文件后,将文件的读权限去掉,依然可以正常读写文件。用open函数打开文件,读取一个字节并输出,然后sleep10秒,再读取一个字节输出,在这10秒间把文件的读权限去掉。发现依然能正常读取。 这说明在从文件描述符read的时候没有再去检察文件权限。

3.18 使ls支持递归

让ls1.c支持递规列出文件。观察系统ls命令,是根据目录一层一层的列出文件,所以用数组做了个栈,列完一层后,再去递归列出其中的目录。

#include	<stdio.h>
#include	<sys/types.h>
#include	<dirent.h>
#include	<unistd.h>
#include	<sys/stat.h>
#include	<string.h>

char * combpath(const char *dir, const char *pf);
int main(int ac, char *av[])
{
	if ( ac == 1 )
		do_ls( "." );
	else
		while ( --ac ){
			printf("%s:\n", *++av );
			do_ls( *av );
		}
	return 0;
}

do_ls( char *dirname )
/*
 *	list files in directory called dirname
 */
{
	DIR		*dir_ptr;		/* the directory */
	struct stat info;
	struct dirent	*direntp;		/* each entry	 */
	char tmpchar;
	char *pathtofile;
	char *stack[128]; //存储目录路径的栈
	int num=0;//栈中的元素个数
	if (stat(dirname, &info) == -1) 
		perror(dirname);
	if ((info.st_mode & S_IFMT) == S_IFDIR)
	{
		printf("%s:\n", dirname );
		if ( ( dir_ptr = opendir( dirname ) ) == NULL )
			fprintf(stderr,"ls1: cannot open %s\n", dirname);
		else
		{
			while ( ( direntp = readdir( dir_ptr ) ) != NULL )
			{
				//将目录名和文件名组成完整路径
				pathtofile=combpath(dirname, direntp->d_name);
				tmpchar = direntp->d_name[0];

				//忽略隐藏文件
				if ( tmpchar == '.' )
					continue;

				if (stat(pathtofile, &info) == -1) 
					perror(pathtofile);

				//如果是目录,压栈
				if ((info.st_mode & S_IFMT) == S_IFDIR)
					stack[num++] = pathtofile;
				printf("%s  ", direntp->d_name );
			}
			printf ("\n");
			//处理栈中的目录
			if (num-- >0)
				do_ls(stack[num]);
			closedir(dir_ptr);
		}
	}
}

char * combpath(const char *dir, const char *pf)
{
	    int len;
		len = strlen(dir);
		char *sep = "/";
		char *pathtofile =  (char *)malloc(strlen(pf)+len+2);
		memset(pathtofile, 0, strlen(pf)+len+2);

		strcat(pathtofile, dir);
		if (pathtofile[len-1] != '/')
		strcat(pathtofile, sep);

		strcat(pathtofile, pf);

		return pathtofile;
}

3.20 用户名和用户id互相转换

getpwnam(username)可以将用户名转换成用户id,getpwuid(uid)可以将用户id转换成用户名,具体用法可以在man手册中查到。chown(path, owner, group)可以修改文件所有者和组。

3.21 utime修改文件最后修改时间和访问时间

系统调用utime可以修改文件最后修改时间和最后访问时间。代码如下:

#include        <stdio.h>
#include        <unistd.h>
#include        <fcntl.h>
#include		<time.h>
#include		<sys/types.h>
#include		<sys/stat.h>
#include		<utime.h>

#define BUFFERSIZE      4096
#define COPYMODE        0644

void oops(char *, char *);

main(int ac, char *av[])
{
        int     in_fd, out_fd, n_chars;
        char    buf[BUFFERSIZE];
		time_t	nowtime;
		struct stat info;
		struct utimbuf timebuf;
						/* check args 	*/
        if ( ac != 3 ){
                fprintf( stderr, "usage: %s source destination\n", *av);
                exit(1);
        }
						/* open files	*/

        if ( (in_fd=open(av[1], O_RDONLY)) == -1 )
                oops("Cannot open ", av[1]);

        if ( (out_fd=creat( av[2], COPYMODE)) == -1 )
                oops( "Cannot creat", av[2]);
	
						/* copy files	*/
		stat(av[1], &info);

        while ( (n_chars = read(in_fd , buf, BUFFERSIZE)) > 0 )
                if ( write( out_fd, buf, n_chars ) != n_chars )
                        oops("Write error to ", av[2]);
		if ( n_chars == -1 )
			oops("Read error from ", av[1]);


        if ( close(in_fd) == -1 || close(out_fd) == -1 )
                oops("Error closing files","");

		timebuf.actime = info.st_atime;
		timebuf.modtime = info.st_mtime;
		utime(av[2], &timebuf);
}

void oops(char *s1, char *s2)
{
        fprintf(stderr,"Error: %s ", s1);
        perror(s2);
        exit(1);
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK