Skip to content

LV040-文件属性与目录

本文主要是C语言——文件属性与目录的基本操作的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

一、目录访问

1. 两个结构体

1.1 DIR

DIR 结构体与 FILE 类似,只是 DIR 存放的是目录相关信息的成员,它的结构,说实话,我只找到了它在 dirent.h 头文件中的声明,详细的结构还是网上搜到的:

c
struct __dirstream
{
    void *__fd;
    char *__data;
    int __entry_data;
    char *__ptr;
    int __entry_ptr;
    size_t __allocation;
    size_t __size;
    __libc_lock_define (, __lock)
};
typedef struct __dirstream DIR;

1.2 dirent

dirent 结构体不仅仅指向目录,还可以指向目录中的具体文件。直接 man 看不到它的定义,使用 man readdir 时可以看到这个结构体定义:

c
struct dirent 
{
    ino_t          d_ino;       /* Inode number 索引节点号*/
    off_t          d_off;       /* Not an offset; see below */
    unsigned short d_reclen;    /* Length of this record */
    unsigned char  d_type;      /* Type of file; not supported by all filesystem types */
    char           d_name[256]; /* Null-terminated filename 文件名,最长255字符*/
};

2. 基本函数

2.1 opendir()

在 linux 下可以使用 man 3 opendir 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <sys/types.h>
#include <dirent.h>

/* 函数声明 */
DIR *opendir(const char *name);

函数说明】该函数打开参数 name 指定的目录, 并返回 DIR* 形态的目录流,。

函数参数

  • name :需要打开的目录名称(可包括路径名)。

返回值】返回值是 DIR * 类型,成功时返回目录流指针;出错时返回 NULL 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <sys/types.h>
#include <dirent.h>

/* 至少应该有的语句 */
DIR * dp;
dp=opendir("/home/hk");

注意事项】 none

2.2 readdir()

在 linux 下可以使用 man 3 readdir 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <dirent.h>

/* 函数声明 */
struct dirent *readdir(DIR *dirp);

函数说明】该函数读取目录流中的内容。

函数参数

  • dirp :已打开目录的目录流指针变量。

返回值】返回值是 struct dirent * 类型, struct dirent 是用来描述目录流中一个目录项的结构体类型。成功时返回目录流 dirp 中的下一个目录项(下个目录进入点);出错或到末尾时返回 NULL 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <dirent.h>

/* 至少应该有的语句 */
DIR * dp;
struct dirent *dt;
dp=opendir("/home/hk");
dt = readdir(dp);
printf("%s\n", dt->d_name);

注意事项】 none

2.3 closedir()

在 linux 下可以使用 man 3 closedir 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <sys/types.h>
#include <dirent.h>

/* 函数声明 */
int closedir(DIR *dirp);

函数说明】该函数关闭已打开的目录文件。

函数参数

  • dirp :已打开目录的目录流指针变量。

返回值】返回值是 int 类型。成功时返回 0 ;出错时返回 EOF 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <sys/types.h>
#include <dirent.h>

/* 至少应该有的语句 */
closedir(dp);

注意事项】 none

3. 使用实例

c
/* 头文件 */
#include <stdio.h>
#include <dirent.h>

int main(int argc,char*argv[])
{
	/* 定义一个目录结构体指针变量 */
	DIR * dp;
	/* 定义一个目录属性结构体变量 */
	struct dirent *dt;
	/* 1. 打开文件 */
	dp=opendir("/home/hk");
	if(dp < 0)
	{
		perror("opendir");
		return -1;
	}
	else
	{
		perror("opendir");
	}
	/* 2. 打印目录名称 */
	while((dt = readdir(dp)) != NULL)
	{
		printf("%s\n", dt->d_name);
	}
	closedir(dp);
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

二、文件访问

1. 文件权限

Linux/Unix 的文件调用权限分为三级 : 文件所有者( Owner )、用户组( Group )、其它用户( Other Users )。只有文件所有者和超级用户可以修改文件或目录的权限。

image-20220511092127123
file type-代表文件
d代表目录
Owner
Group
Other Users
rread(可读)
wwrite(可写)
xexecute(可执行)
-无权限
例如:

image-20260311112220105

对于文件的权限还可以使用三位 8 进制数表示:

image-20220511092515505

2. 基本函数

2.1 chmod()

在 linux 下可以使用 man 2 chmod命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <sys/stat.h>

/* 函数声明 */
int chmod(const char *pathname, mode_t mode);

函数说明】该函数用于修改文件权限。

函数参数

  • pathname :需要修改权限的文件的文件名(可以包含路径)。
  • mode :要修改的权限(一般为四位十六进制数,后三位代表要修改的权限)。

返回值】返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <sys/stat.h>

/* 至少应该有的语句 */
int fd;
char buff[32]={0};
int ret;
fd = open("02read_write.txt",O_RDWR | O_CREAT | O_APPEND, 0666);
ret = read(fd, buff, 32);

注意事项

(1)在虚拟机中使用 Linux 时,修改共享文件夹 /mnt/hgfs/file_name 中的文件权限可能会无效,因为这里边的文件同时也是 Windows 下的文件。

(2)可以在终端使用: ll file_name 命令来单独查看某个文件的权限 。

2.2 fchmod()

在 linux 下可以使用 man 2 fchmod命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <sys/stat.h>

/* 函数声明 */
int fchmod(int fd, mode_t mode);

函数说明】该函数修改文件描述符 fd 指向文件的文件权限。

函数参数

  • fd :文件描述符。
  • mode :要修改的权限(一般为四位十六进制数,后三位代表要修改的权限)。

返回值】 返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include

/* 至少应该有的语句 */

注意事项】 none

3. 使用实例

c
/* 头文件 */
#include <stdio.h>
/* chmod 函数 */
#include <sys/stat.h>

void test_chmod();

int main(int argc,char*argv[])
{
	test_chmod();
	return 0;
}

/**
 * @Function: int chmod(const char *path, mode_t mode);
 * @Description: 成功时返回0;出错时返回EOF
 */
void test_chmod()
{
	int ret;
	ret = chmod("/home/hk/3Test/test.vim",0444);
	if(ret < 0)
	{
		perror("chmod");
		return;
	}
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

三、文件属性

1. stat 结构体

我们可以在linux内核源码的 include/uapi/asm-generic/stat.h中找到这个结构体。 这个结构体成员是文件的各种信息:

c
struct stat {
	unsigned long	st_dev;		/* Device.  */
	unsigned long	st_ino;		/* File serial number.  */
	unsigned int	st_mode;	/* File mode.  */
	unsigned int	st_nlink;	/* Link count.  */
	unsigned int	st_uid;		/* User ID of the file's owner.  */
	unsigned int	st_gid;		/* Group ID of the file's group. */
	unsigned long	st_rdev;	/* Device number, if device.  */
	unsigned long	__pad1;
	long		   st_size;	    /* Size of file, in bytes.  */
	int		       st_blksize;	/* Optimal block size for I/O.  */
	int		       __pad2;
	long		   st_blocks;	/* Number 512-byte blocks allocated. */
	long		   st_atime;	/* Time of last access.  */
	unsigned long  st_atime_nsec;
	long		   st_mtime;	/* Time of last modification.  */
	unsigned long  st_mtime_nsec;
	long		   st_ctime;	/* Time of last status change.  */
	unsigned long  st_ctime_nsec;
	unsigned int   __unused4;
	unsigned int   __unused5;
};

其中主要就是st_mode这个成员,它包含了文件的类型和存取的权限。 st_mode 中有 9 位是代表文件的权限的,我们可以将 1 左移再与 st_mode 做 & 运算,即可判断每位为 1 或者为 0 。

image-20220511103048184

st_mode 文件权限部分如下:

S_ISUID04000set-user-ID bit
S_ISGID02000set-group-ID bit
S_ISVTX01000sticky bit
S_IRWXU00700owner has read, write, and execute permission
S_IRUSR00400owner has read permission
S_IWUSR00200owner has write permission
S_IXUSR00100owner has execute permission
S_IRWXG00070group has read, write, and execute permission
S_IRGRP00040group has read permission
S_IWGRP00020group has write permission
S_IXGRP00010group has execute permission
S_IRWXO00007others (not in group) have read, write, and execute permission
S_IROTH00004others have read permission
S_IWOTH00002others have write permission
S_IXOTH00001others have execute permission
系统提供了一些宏,可以用这些宏来识别 st_mode 的值,以判定文件的类型和权限。我们可以直接通过宏的返回值判断文件的类型,也可以使用 st_mode & S_IFMT 的值来判断文件类型,例如,
c
stat(pathname, &sb);
if ((sb.st_mode & S_IFMT) == S_IFREG) 
{
    /* Handle regular file */
}

st_mode 文件类型的宏如下:

S_IFMT0170000文件类型位字段的位掩码
S_IFSOCK0140000S_ISSOCK(st_mode) 是否SOCKET文件,是返回1,否则返回0
S_IFLNK 0120000S_ISLNK(st_mode) 是否链接文件,是返回1,否则返回0
S_IFREG 0100000S_ISREG(st_mode) 是否常规文件,是返回1,否则返回0
S_IFBLK 0060000S_ISBLK(st_mode) 是否块设备,是返回1,否则返回0
S_IFDIR 0040000S_ISDIR(st_mode) 是否目录,是返回1,否则返回0
S_IFCHR 0020000S_ISCHR(st_mode) 是否字符设备,是返回1,否则返回0
S_IFIFO 0010000S_ISFIFO(st_mode) 是否FIFO文件,是返回1,否则返回0

2. 基本函数

2.1 stat()

在 linux 下可以使用 man 2 stat 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 函数声明 */
int stat(const char *pathname, struct stat *statbuf);

函数说明】该函数用于获取文件信息。

函数参数

  • pathname :文件名(可以包含路径)。
  • statbuf : struct stat 类型的结构体,用于存放文件属性信息。

返回值】返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 至少应该有的语句 */
struct stat file_attr;
int ret;
ret = stat("03file_attributes.c", &file_attr);

注意事项】 如果 pathname 是符号链接,那么 stat 获取的是目标文件的属性。

2.2 lstat()

在 linux 下可以使用 man 2 lstat 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 函数声明 */
int lstat(const char *pathname, struct stat *statbuf);

函数说明】该函数用于获取文件信息。

函数参数

  • pathname :文件名(可以包含路径)。
  • statbuf : struct stat 类型的结构体,用于存放文件属性信息。

返回值】返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 至少应该有的语句 */
struct stat file_attr;
int ret;
ret = lstat("03file_attributes.c", &file_attr);

注意事项】 如果 pathname 是符号链接,那么 lstat 获取的是链接文件的属性。

2.3 fstat()

在 linux 下可以使用 man 2 fstat 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 函数声明 */
int fstat(int fd, struct stat *statbuf);

函数说明】该函数可以由文件描述符取得文件状态。

函数参数

  • fd :已经打开文件的文件描述符。
  • statbuf : struct stat 类型的结构体,用于存放文件属性信息。

返回值】返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 至少应该有的语句 */
struct stat file_attr;
int ret;
int fd;
fd = open( "03file_attributes.c", O_RDONLY);
ret = fstat(fd, &file_attr);

注意事项】 none

3. 使用实例

c
/* 头文件 */
#include <stdio.h>
/* rstat lstat fstat函数函数 */
#include <sys/stat.h>
/* time */
#include <unistd.h>
#include <time.h>
void test_stat();

int main(int argc,char*argv[])
{
	test_stat();
	return 0;
}

/**
 * @Function: int  stat(const char *path, struct stat *buf);
 *		      int  lstat(const char *path, struct stat *buf);
 * @Description: 如果path是符号链接stat获取的是目标文件的属性;
 *               而lstat获取的是链接文件的属性
 */
void test_stat()
{
	/* 定义一个文件属性结构体变量 */
	struct stat file_attr;
	int ret;
	int i;
	struct tm *t;
	/* 1. 获取文件属性保存到结构体变量中 */
	ret = stat("03file_attributes.c", &file_attr);
	/* 2. 判断是否获取成功 */
	if(ret < 0)
	{
		perror("stat");
		return;
	}
	/* 3. 打印文件存取类型 */
	if(S_ISREG(file_attr.st_mode))
	{
		printf("-");
	}
	if(S_ISDIR(file_attr.st_mode))
	{
		printf("d");
	}
	if(S_ISCHR(file_attr.st_mode))
	{
		printf("c");
	}
	if(S_ISBLK(file_attr.st_mode))
	{
		printf("b");
	}
	if(S_ISFIFO(file_attr.st_mode))
	{
		printf("p");
	}
	if(S_ISSOCK(file_attr.st_mode))
	{
		printf("s");
	}
	/* 4. 打印文件权限 */
	for(i = 8; i >= 0; i--)
	{
		if(file_attr.st_mode & (1 << i))
		{
			switch(i % 3)
			{
				case 0:
					printf("x");
					break;
				case 1:
					printf("w");
					break;
				case 2:
					printf("r");
					break;
			}
		}
		else
		{
			printf("-");
		}
	}
	/* 5. 文件字节数(文件大小) */
	printf(" %d",(int)file_attr.st_size);
	/* 6. 打印文件修改时间 */
	t = localtime(&file_attr.st_ctime);
	printf(" %d-%d-%d %d:%d",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min);
	printf(" 03file_attributes.c\n");
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序