Linux学习笔记——系统文件与目录操作函数

文章目录

  • 文件操作函数
    • 获取文件属性—stat、lstat、fstat
  • 目录操作
    • readdir 函数
    • dup和dup2
    • fcntl


文件操作函数

获取文件属性—stat、lstat、fstat

其函数原型:
Linux学习笔记——系统文件与目录操作函数_第1张图片
这里 struct stat结构体如下:

struct stat {
    dev_t         st_dev;       //文件的设备编号
    ino_t         st_ino;       //节点
    mode_t        st_mode;      //文件的类型和存取的权限
    nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1
    uid_t         st_uid;       //用户ID
    gid_t         st_gid;       //组ID
    dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号
    off_t         st_size;      //文件字节数(文件大小)
    blksize_t     st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
    blkcnt_t      st_blocks;    //块数
    time_t        st_atime;     //最后一次访问时间
    time_t        st_mtime;     //最后一次修改时间
    time_t        st_ctime;     //最后一次改变时间(指属性)
};

st_mode 该变量占 2 byte,共 16 位:

(1) 掩码的使用: st_mode  &   掩码

(2) 其他人权限( 0-2 bit )
	(a) S_IROTH    00004                 读权限           
	(b) S_IWOTH    00002                写权限                     掩码:S_IRWXO  00007
	(c) S_IXOTH    00001                  执行权限

(3) 所属组权限(3-5bit)
	(a) S_IRWXG    00070              读权限           
	(b) S_IRGRP    00040              写权限                     掩码:S_RWXG  00070
	(c) S_IXGRP    00010               执行权限

(4) 文件所有者权限(6-8bit)
	(a) S_IRUSR    00400               读权限           
	(b) S_IWUSR    00200              写权限                     掩码:S_IRWXU  00700
	(c) S_IXUSR    00100                执行权限

(5) 文件特权位(9-11bit)
	(a) S_ISUID    0004000            设置用户ID                 
	(b) S_ISGID    0002000            设置组ID                   文件特权位很少用
	(c) S_ISVTX    0001000          设置黏住位

(6) 文件类型(12-15bit)
	(a) S_IFSOCK   0140000   socket(套接字)
	(b) S_IFLNK    0120000   symbolic link(符号链接--软连接)
	(c) S_IFREG    0100000   regular file(普通文件)
	(d) S_IFBLK    0060000   block device(块设备)                                掩码:S_IFMT 017000
	(e) S_IFDIR    0040000   directory(目录)
	(f) S_IFCHR    0020000   character device(字符设备)
	(g) S_IFIFO    0010000   FIFO(管道)

以上三个获取文件属性的函数 若成功,返回0;若失败,返回 -1

stat、lstat、fstat之间的区别

  1. fstat 函数:系统调用的是一个文件描述符,而另外两个则直接接收文件路径。文件描述符是我们用 open 系统调用后得到的,而文件全路径直接写就可以了。

  2. stat 函数与 lstat 函数的区别: 当一个文件是符号链接时,lstat 函数返回的是该符号链接本身的信息(穿透);而 stat 函数返回的是该链接指向文件的信息(不穿透)。

#include
#include
#include
#include
#include

int main(int argc,char *argv[])
{
        if( argc<2 )
        {
                perror("a.out ");
                exit(1);
        }

        struct stat st;

        int ret = lstat(argv[1],&st);
        if( ret == -1)
        {
                perror("lstat");
                exit(1);
        }

        int size = st.st_size;

        printf("file size = %d\n",size);

        return 0;
}



下面使用 stat 实现一个简单的 ls -l 指令:

#include
#include
#include
#include     // 所有者信息
#include     // 所属组信息
#include
#include
#include
#include

int main(int argc,char *argv[])
{
        if( argc<2 )
        {
                perror("./a.out filename\n");
                exit(1);
        }

        struct stat st;
        int i;
        for( i = 1; i<argc; i++)
        {

                int ret = stat(argv[i],&st);   // 获取文件或者目录的所有信息存储于 st 结构体中

                if( ret == -1 )
                {
                        perror("stat");
                        exit(1);
                }

                // 存储文件类型和访问权限

                char perms[11] = {0};

                // 判断文件类型

                switch( st.st_mode & S_IFMT )
                {
                        case S_IFSOCK:   // 套接字文件
                                perms[0] = 's';
                                break;
                        case S_IFLNK:    // 软连接文件
                                perms[0] = 'l';
                                break;
                        case S_IFREG:    // 普通文件
                                perms[0] = '-';
                                break;
                        case S_IFBLK:    // 块设备文件
                                perms[0] = 'b';
                                break;
                        case S_IFDIR:    // 目录文件

                                perms[0] = 'd';
                                break;
                        case S_IFCHR:    // 字符设备文件

                                perms[0] = 'c';
                                break;
                        case S_IFIFO:    // 管道文件

                                perms[0] = 'p';
                                break;
                        default:
                                break;

                }

                // 判断文件的访问权限
                // 文件的所有者
                perms[1] = (st.st_mode & S_IRUSR) ? 'r':'-';
                perms[2] = (st.st_mode & S_IWUSR) ? 'w':'-';
                perms[3] = (st.st_mode & S_IXUSR) ? 'x':'-';

                // 文件的所属组
                perms[4] = (st.st_mode & S_IRGRP) ? 'r':'-';
                perms[5] = (st.st_mode & S_IWGRP) ? 'w':'-';
                perms[6] = (st.st_mode & S_IXGRP) ? 'x':'-';

                // 文件的其他用户

                perms[7] = (st.st_mode & S_IROTH) ? 'r':'-';
                perms[8] = (st.st_mode & S_IWOTH) ? 'w':'-';
                perms[9] = (st.st_mode & S_IXOTH) ? 'x':'-';

                // 硬链接计数

                int nums = st.st_nlink;

                // 文件所有者

                char *fileuser = getpwuid(st.st_uid)->pw_name;

                // 文件所属组
                char *filegroup = getgrgid(st.st_gid)->gr_name;

                // 文件大小
                int size = (int)st.st_size;

                // 文件修改时间

                char *time = ctime(&st.st_mtime);
                char mtime[512]="";
                strncpy(mtime,time,strlen(time)-1);

                // 保存输出信息格式
                char buf[1024]={0};

                // 把对应信息按格式输出到 buf 中
                sprintf(buf,"%s %d %s %s      %d %s %s",perms,nums,fileuser,filegroup,size,mtime,argv[i]);

                // 打印 buf
                printf("%s\n",buf);

                //      drwxrwxr-x 3 arrayli arrayli      4096 11月 13 23:19 day05
                //      -rw-r--r-- 1 arrayli arrayli      8980 11月  7 22:05 examples.desktop

        }

        return 0;
}

ctime 函数是将日历时间(这里是一个结构体)转化为字符串形式。

运行结果如下:

文件操作函数还有 accesschmodchowntruncatelinkunlinkrename 等,用法都比较简单,参考最上面的图片以及man文档即可。


目录操作

Linux学习笔记——系统文件与目录操作函数_第2张图片

readdir 函数

readdir 函数主要用于读目录,其函数原型如下:

dirent 结构体内容如下:

struct dirent {
	ino_t          d_ino;       /* inode number */                  // 目录进入点的 inode
	off_t          d_off;       /* not an offset; see NOTES */      // 目录文件头开始至此目录进入点的位移
	unsigned short d_reclen;    /* length of this record */         // d_name的长度,不包括NULL字符
    unsigned char  d_type;      /* type of file; not supported      // d_name所指的文件类型 
                                              by all filesystem types */
    char           d_name[256]; /* filename */                      // 文件名
         };
 

其中 dtype 有八种类型

(1) DT_BLK      This is a block device.           块设备
(2) DT_CHR      This is a character device.       字符设备
(3) DT_DIR      This is a directory.              目录
(4) DT_FIFO     This is a named pipe (FIFO).      管道
(5) DT_LNK      This is a symbolic link.          软链接
(6) DT_REG      This is a regular file.           普通文件
(7) DT_SOCK     This is a UNIX domain socket.     套接字
(8) DT_UNKNOWN  The file type is unknown.         未知类型

下面实现递归读取指定目录所有文件,并输出总的文件数量。

#include
#include
#include
#include
#include
#include
#include
 
 
// 获取 root 目录下的文件个数
int get_file_count(char *root)
{
	// open dir
	DIR * dir = NULL;
	dir = opendir(root);
	if( NULL == dir )
	{
		perror("opendir");
		exit(1);
	}
 
	// 遍历当前打开的目录
	struct dirent* ptr = NULL;
	char path[1024]={0};
	int total = 0;
	while( (ptr = readdir(dir) )!= NULL)
	{
		// 过滤掉 . 和 ..
		if( strcmp(ptr->d_name,".") == 0 || strcmp(ptr->d_name,"..") == 0 )
		{		
			continue;	
		}
	
		// 如果是目录,递归读目录
		if(ptr->d_type == DT_DIR)
		{
			sprintf(path,"%s/%s",root,ptr->d_name);
			total += get_file_count(path);
		}
 
		// 如果是普通文件
		if( ptr->d_type == DT_REG )
		{
				total++;	
		}
	}
	
	// 关闭目录
	closedir(dir);
	return total;
}
 
int main(int argc,char *argv[])
{
	if( argc<2 )
	{
		perror("./a.out dir\n");
		exit(1);	
	}
 
	// 获取文件个数
	int count =	get_file_count(argv[1]);
 
	printf("%s has file numbers : %d\n",argv[1],count);	
	return 0;
}

dup和dup2

Linux学习笔记——系统文件与目录操作函数_第3张图片

#include
#include
#include
#include
#include
#include
#include
 
int main(void)
{
	int fd =open("a.txt",O_RDWR);
	if( fd == -1 )
	{
		perror("open");
		exit(1);
	}
 
	printf("file open fd = %d\n",fd);
 
	// 找到进程文件描述符表   ======= 第一个========== 可用的文件描述符
	// 将参数指定的文件复制到该描述后          返回这个描述符
 
	int ret = dup(fd);
	if( fd == -1 )
	{
		perror("dup");
		exit(1);
	}
 
	printf(" dup fd = %d\n",ret);
	char *buf = "你是猴子请来的救兵吗??\n";
	char *buf1 = "你大爷的,我是程序猿!!!\n";
 
	write(fd,buf,strlen(buf));
	write(ret,buf1,strlen(buf1));
 
	close(fd);
	
	return 0;
}

Linux学习笔记——系统文件与目录操作函数_第4张图片
可以看到 newfd 与 oldfd 对应同一个文件描述,其文件偏移是一样的。

#include
#include
#include
#include
#include
#include
#include
 
int main(void)
{
	int fd =open("english.txt",O_RDWR);
	if( fd == -1 )
	{
		perror("open");
		exit(1);
	}
 
	int fd1 =open("a.txt",O_RDWR);
	if( fd1 == -1 )	
	{
		perror("open");
		exit(1);
	}
 
	printf("fd = %d\n",fd);
	printf("fd1 = %d\n",fd1);
 
	int ret = dup2(fd1, fd);
	if( ret == -1 )
	{	
		perror("dup2");
		exit(1);
	}
 
	printf(" current fd = %d\n",ret);
 
	char *buf = "主要看气质 !!!!!!!!!!!!!!!!!\n";
 
	write(fd,buf,strlen(buf));
	write(fd1,"hello world!",12);
 
	close(fd);
	close(fd1);
 
	return 0;
}

Linux学习笔记——系统文件与目录操作函数_第5张图片
可以看到 dup2()newfd 关闭了,并且 newfdoldfd 对应同一个文件。

fcntl

Linux学习笔记——系统文件与目录操作函数_第6张图片
fcntl 可以改变已打开文件的属性,下面使用 fcntl 将一个只写文件改为追加写。

#include
#include
#include
#include
#include
#include
#include
 
int main(void)
{
	int flag;
	int fd;
 
	// 测试字符串
	char *q ="社会主义好哇!!!\n";
 
	// 以只写方式打开文件
 
	fd = open("test.txt",O_WRONLY);
	if( fd == -1 )
	{
		perror("open");
		exit(1);
	
	}
 
	//	使用 F_GETFL 命令得到文件状态标志
	int flags = fcntl(fd,F_GETFL,0); 
	if( flags == -1 )
	{
		perror("fcntl");
		exit(1);
	}![请添加图片描述](https://img-blog.csdnimg.cn/daabca96aa5e4816ada651c8191074ff.png)

 
	// 将文件状态修改为追加写
 
	if( fcntl(fd,F_SETFL,O_APPEND) == -1 )
	{
		perror("fcntl");
		exit(1);
	}
 
	// 输入新的内容,该内容会追加到旧内容对的后面
 
	if( write(fd,q,strlen(q)) == -1 )
	{
		perror("write");
		exit(1);
	}
	
	return 0;
}

正常只写文件会覆盖原有文件,而改为追加写,则会在在原有文件之后继续写。
Linux学习笔记——系统文件与目录操作函数_第7张图片

你可能感兴趣的:(Linux与Shell编程,linux,学习,服务器)