1 stat结构体
在linux下面,有一个stat结构体是用来保存一个文件的状态或属性信息的。 我们在终端下输入man 2 stat
查看这个结构体如下所示:
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
nlink_t st_nlink; /* Number of hard links */
uid_t st_uid; /* User ID of owner */
gid_t st_gid; /* Group ID of owner */
dev_t st_rdev; /* Device ID (if special file) */
off_t st_size; /* Total size, in bytes */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
struct timespec st_ctim; /* Time of last status change */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
让我们再来看看这个结构体各成员的含义:
- dev_t st_dev; 用来标识容纳该文件(就是你的那个设备的设备号)。 比如该文件可能会存在于某个硬盘或者某个U盘上 ,那么这个硬盘或U盘会有一个设备号,st_dev则负责保存这个设备号。
- ino_t st_ino; 该文件的inode编号 ,文件系统中的每个文件都有一个唯一的inode号。
- mode_t st_mode; 文件类型与权限。
- nlink_t st_nlink; 该文件的硬链接数。
- uid_t st_uid; 文件拥有者的id 。
- gid_t st_gid; 文件拥有者的组id 。
- dev_t st_rdev; 设备号 (如果该文件是设备则有)。
- off_t st_size; 文件的大小,对于普通文件来说, 意味着就是文件内容的字节数。对于符号链接(软链接)其文件内容是指向的那个文件的文件名的字节数。
- blksize_t st_blksize; 块大小(与具体的硬件设备有关 512字节为一块)。
- blkcnt_t st_blocks; 该文件占多少块。
- struct timespec st_atim; 最后访问“文件内容”的时间 。
- struct timespec st_mtim; 最后修改“文件内容”的时间。
- struct timespec st_ctim; 最后修改“文件属性”的时间。
Linux下对inode和块的理解
其中我们最常用的是这个结构体成员st_mode(文件类型与权限),那么怎么去获取文件的类型和权限呢? 宏,Linux提供了宏来解析该成员变量,比如:
struct stat st;
/*
S_ISREG(st.st_mode) 为真表示该文件是普通文件 -
S_ISDIR(st.st_mode) 为真表示该文件是目录文件 d
S_ISCHR(st.st_mode) 为真表示该文件是字符设备 c
S_ISBLK(st.st_mode) 为真则表示该文件是块设备 b
S_ISFIFO(st.st_mode)为真则表示该文件是管道文件 p
S_ISLNK(st.st_mode) 为真则表示该文件是符号链接文件 l
S_ISSOCK(st.st_mode)为真则表示该文件是套接字文件 s
*/上面的宏用来解析对应的文件的类型
/*st.st_mode还包含了文件的权限位,可以通过如下代码来解析
if(st.st_mode & S_IRUSR)
{
user有可读的权限
}
*/还有一些权限位也可以通过上述代码的方式来判断
//S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP
//S_IROTH S_IWOTH S_IXOTH
2 获取文件属性
NAME
stat, fstat, lstat - get file statusSYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h> int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
int stat(const char *pathname, struct stat *statbuf);
- 功能及参数:stat是用来获取pathname指定的那个文件的属性信息的, 获取的信息用statbuf指向的空间保存。注意当pathname是 一个链接文件的时候,stat是获取符号链接文件指向的那个文件的属性信息。
- 返回值:
成功返回0
失败返回-1 并且errno被设置
int fstat(int fd, struct stat *statbuf);
- 功能及参数:fstat也是用来获取文件的属性信息的 ,只不过 fstat需要一个文件描述符来执行一个文件,所以要先调用open函数打开 它返回一个文件描述符。
- 返回值:
成功返回0
失败返回-1 并且errno被设置
int lstat(const char *pathname, struct stat *statbuf);
- 功能及参数:lstat的功能与stat是一样的 。只不过要注意当pathname是一个链接文件的时候, lstat获取的是符号链接文件本身的属 性信息。
- 返回值:
成功返回0
失败返回-1 并且errno被设置
注意区分stat和lstat:
1n -s 1.txt 2.txt //创建1.txt的一个符号(软)连接 2.txt
stat(2.txt,statbuf) --> statbuf结构体保存的是1.txt的属性
lstat(2.txt,statbuf) -->statbuf结构体保存的是2.txt的属性
例子:获取文件类型、属性和大小
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc , char * argv[])
{
//获取指定文件的属性 并保存在st这个结构体中
struct stat st;
stat(argv[1] ,&st);
/*
struct stat st;
struct stat * p = &st;
stat(argv[1] ,p);
*/
//通过对st结构体成员变量的分析,来解析他的类型、权限和大小
//解析类型
if( S_ISREG(st.st_mode) )
{
printf("is reg file!\n");
}
else if(S_ISDIR(st.st_mode))
{
printf("is dir file!\n");
}
//解析权限
if( st.st_mode & S_IRUSR)
{
printf("uesr have read!\n");
}
//解析大小
printf("file size = %ld\n",st.st_size);
}
3 目录操作
- 在linux下面, 目录也是一个文件 ,同样可以通过open打开它 。
- 打开之后我们可以得到一个文件描述符, 这个文件描述符描述的是一个目录。
- 但是我们在用read(fd,…)去读一个目录的内容时,会报错,所以在linux不能用read去读一个目录 。
- 目录文件里面的内容到底是什么了?为什么我们不能去read它呢?
目录和普通文件的区别 ,在linux下面 ,任何一个文件要存在必须有一个inode ,普通文件需要一个inode。目录文件同样也需要一个inode, 目录文件中的内容应该是什么呢?
我们联想一下windows 在 windows下我们双击普通文件相当于就是打开了这个文件, 我们看到的就是该文件的内容 ,同理我们双击文件夹也应该打开了这个文件夹(目录) 理应我们看到就是文件夹的内容 ,那么这就意味着我们访问一个文件夹 ,其实得到的就是这个文件夹下子文件或者子文件夹的属性信息。 linux也是如此。
linux下打开一个目录, 目录的内容也是该目录下包含的子目录或者子文件夹的属性信息!
那么怎么保存目录下子文件或者子目录的属性信息了?
通过目录项数组, 也就是说目录文件的内容其实就是一个目录项数组 。
“目录项”是什么呢? 我们把目录下的每一个子文件或者子目录都称之为一个目录项。
目录项数组的本质就是一个结构体数组 ,每一个目录项都对应一个结构体,这个结构体的类型是 struct dirent,我们如果想要知道一个目录下面的文件内容 ,那么就必须要去读目录项。
打开一个目录 opendir
NAME
opendir, fdopendir - open a directory
SYNOPSIS
#include <sys/types.h>
#include <dirent.h> DIR *opendir(const char *name);
DIR *fdopendir(int fd);
函数功能:打开一个目录参数列表: name: 你要打开的目录的路径名
fd: 你要打开的目录的文件描述符 (先要open它, 以只读方式)
返回值:
成功返回一个DIR*类型的指针, 这个指针就代表这个打开的目录
失败返回NULL 同时errno被设置
在linux下面用结构体DIR来描述一个已经打开的目录 ,DIR*则意味着指向一个已经成功打开的目录,后续的读操作需要用到该指针 。
读一个目录中的目录项 readdir
NAME
readdir - read a directorySYNOPSIS
#include <dirent.h>
struct dirent *readdir(DIR *dirp);函数功能: 从目录中读取目录项。
参数列表:
dirp : 表示你要从哪个打开的目录中读取目录项 ,就是opendir的返回值。
返回值:
返回下一个目录项的指针, 如果读完了整个目录项,就返回NULL 。
readdir用来从dirp指向的打开的目录中, 返回下一个目录项(struct dirent)的指针 。 一个目录下有多个目录项(一个子文件或者子文件夹都会对应一个目录项),每调用一次readdir就会返回下一个目录项的指针, 直到返回NULL。
struct dirent {
ino_t d_ino; /* Inode number */
//inode的编号
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 */
//该目录项指向的那个文件的名字
};
该结构体中的成员变量只有d_ino 、d_name是所有linux系统都支持的。
关闭一个目录 closedir
NAME
closedir - close a directorySYNOPSIS
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);函数功能: 关闭一个已经打开的目录
参数列表:
dirp : 你要关闭哪个打开的目录
返回值:
成功返回0
失败返回-1 并且errno被设置
例子:把一个目录下所有的文件的文件名打印出来
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
//格式: ./main dir_name
int main(int argc , char * argv[])
{
//1.打开目录
DIR * dir = opendir(argv[1]);
if(dir == NULL)
{
perror("opendir error");
return -1;
}
//2.不停地读目录项
struct dirent * dirp = NULL;
while(1)
{
dirp = readdir(dir);
if(dirp == NULL)
{
break;
}
//打印读到的目录项指向的文件的名字 跳过.和..这两个文件夹
if( strcmp(dirp->d_name ,".") == 0 || strcmp(dirp->d_name ,"..") == 0 )
{
continue;
}
printf("%s\n",dirp->d_name);
}
//3.关闭目录
closedir(dir);
return 0;
}
例子:一个目录下所有普通文件的大小之和(目录下有目录 ,目录下的目录下还有文件 – 递归方式)
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int get_dir_size(const char * pathname)
{
int size = 0 ; //保存目录的大小
//打开目录
DIR * dir = opendir( pathname);
if(dir == NULL)
{
perror("opendir error");
return -1;
}
//不停地读目录项 判断
struct dirent * dirp = NULL;
while(1)
{
dirp = readdir(dir);
if(dirp == NULL)
{
break;
}
//如果是.和.. 跳过它
if( strcmp(dirp->d_name ,".") == 0 || strcmp(dirp->d_name ,"..") == 0 )
{
continue;
}
//获取到带路径的文件名 路径:pathname 名字:dirp->d_name
char filename[512]={0};
sprintf(filename, "%s/%s",pathname,dirp->d_name);//拼接路径保存到filename
//printf("%s/%s",pathname , dirp->d_name);
//获取filename的属性信息
struct stat st;
int ret = stat(filename,&st);
if(ret == -1)
{
perror("stat error");
return -1;
}
if(S_ISDIR(st.st_mode))//如果是一个目录
{
size += get_dir_size(filename);
}
else if(S_ISREG(st.st_mode))
{
size += st.st_size;
}
}
closedir(dir);
return size;
}
int main(int argc , char * argv[])
{
printf("size = %d\n",get_dir_size(argv[1]));
}