前言
UNIX/Linux 的缔造者们将数据的 来源和目标 都抽象为 文件,所以在 UNIX/Linux 系统中 一切皆文件
一切皆文件 不仅仅对磁盘,还包括鼠标,键盘,显示器这些设备
那么目录算不算文件呢?当然算,目录是一种特殊的文件,目录里存放的内容是子目录和文件的索引信息
这里分享一下我在学习文件目录过程中的笔记和心得
概要
代码示例
要求
统计 /home/emacs/c 下有几个C语言源文件 .c ,并找出文件大小最大的那个文件名
Tip: 要求掌握opendir,readdir,closedir,rewinddir用法
代码示例
#include <stdio.h>
#include <dirent.h> //DIR,opendir,readdir,rewinddir,closedir 的定义和声明都在这个头文件里面
#include <string.h>
#include <sys/stat.h> // stat 结构体的定义在里面
#define MAX 1000
int main()
{
char *dirpath="/home/emacs/c";
DIR *dir=NULL;
struct dirent *de=NULL;
struct stat fs;
int len=0,i=0;
unsigned long maxsize=0;
char filename[MAX]; //各种变量的声明与初始化
if(NULL == (dir=opendir(dirpath))) //打开目录文件并且将目录指针赋给dir,出错则提示并且返回
{
printf("open dir error:%s\n",dirpath);
return -1;
}
while(NULL != (de=readdir(dir))) //遍历目录中的每个条目
{
len=strlen(de->d_name); //获取一个条目的文件名长度存入到len中
if(de->d_name[len-1]=='c' && de->d_name[len-2]=='.' ) //如果一个条目是以'.c'结尾的
{
i++; //使用i进行计数
strcpy(filename,dirpath);
strcat(filename,"/");
strcat(filename,de->d_name); //拼接成一个完整的绝对路径
stat(filename,&fs); //将这个文件的属性信息存入到fs结构体中
if(maxsize < fs.st_size)maxsize=fs.st_size; //总是将最大的文件大小值存入到maxsize变量中
}
}
printf("the total number of .c file is :%d \nthe max size is:%ld\nthe max size .c file are :\n",i,maxsize); //打印出'.c'文件个数,和最大的文件大小
rewinddir(dir); //重新定位目录指针到目录开始处
while(NULL != (de=readdir(dir))) //遍历目录中的每个条目
{
len=strlen(de->d_name); //获取一个条目的文件名长度存入到len中
if(de->d_name[len-1]=='c' && de->d_name[len-2]=='.' ) //如果一个条目是以'.c'结尾的
{
strcpy(filename,dirpath);
strcat(filename,"/");
strcat(filename,de->d_name); //拼接成一个完整的绝对路径
stat(filename,&fs); //将这个文件的属性信息存入到fs结构体中
if(fs.st_size == maxsize)printf("%s\n",filename); //将大小为maxsize的条目进行打印
}
}
closedir(dir); //关闭目录
return 0;
}
编译执行
emacs@ubuntu:~/c$ alias gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc filestat.x filestat.c
emacs@ubuntu:~/c$ ./filestat.x
the total number of .c file is :48
the max size is:5686
the max size .c file are :
/home/emacs/c/sqlite.c
emacs@ubuntu:~/c$
emacs@ubuntu:~/c$ ll -trl *.c | sort -nk 5 | tail -n 5
-rw-r--r-- 1 emacs emacs 5250 2016-12-29 08:23 ftpclient.c
-rw-r--r-- 1 emacs emacs 5352 2016-12-14 01:04 6d.c
-rw-r--r-- 1 emacs emacs 5352 2016-12-14 01:06 toblog.c
-rw-r--r-- 1 emacs emacs 5407 2016-12-29 06:44 ftpserver.c
-rw-r--r-- 1 emacs emacs 5686 2016-12-29 01:39 sqlite.c
emacs@ubuntu:~/c$
编译执行过程中没有报错,从结果来看,符合预期
DIR
代码中有一个这样的定义
DIR *dir=NULL;
DIR 是一种新的结构体
emacs@ubuntu:/usr/include$ grep DIR dirent.h | grep typedef
typedef struct __dirstream DIR;
emacs@ubuntu:/usr/include$ grep dirstream * -r
dirent.h:typedef struct __dirstream DIR;
emacs@ubuntu:/usr/include$
可知 DIR 是 __dirstream
的别名,但是头文件中没有有关于 __dirstream
的详细定义
通过查资料,得知 __dirstream
有如下定义
struct __dirstream
{
void *__fd; //`struct hurd_fd' pointer for descriptor.
char *__data; //Directory block.
int __entry_data; //Entry number `__data' corresponds to.
char *__ptr; //Current pointer into the block.
int __entry_ptr; //Entry number `__ptr' corresponds to.
size_t __allocation; //Space allocated for the block.
size_t __size; //Total valid data in the block.
__libc_lock_define (, __lock) //Mutex lock for this structure.
};
总体看来,就是关于一个目录的描述信息
常用的目录操作函数
在 dirent.h 中对常用的目录操作函数作了声明
/* Open a directory stream on NAME.
Return a DIR stream on the directory, or NULL if it could not be opened.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern DIR *opendir (__const char *__name) __nonnull ((1));
/* Close the directory stream DIRP.
Return 0 if successful, -1 if not.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern int closedir (DIR *__dirp) __nonnull ((1));
/* Read a directory entry from DIRP. Return a pointer to a `struct
dirent' describing the entry, or NULL for EOF or error. The
storage returned may be overwritten by a later readdir call on the
same DIR stream.
If the Large File Support API is selected we have to use the
appropriate interface.
This function is a possible cancellation point and therefore not
marked with __THROW. */
#ifndef __USE_FILE_OFFSET64
extern struct dirent *readdir (DIR *__dirp) __nonnull ((1));
#else
/* Rewind DIRP to the beginning of the directory. */
extern void rewinddir (DIR *__dirp) __THROW __nonnull ((1));
- opendir
- closedir
- readdir
- rewinddir
除了 opendir 接收一个文件名字符串,返回一个 DIR 型指针,其它几个函数的参数都是接受一个 DIR 型的指针
dirent
代码中有一个这样的定义
struct dirent *de=NULL;
dirent 是一种新的结构体
在头文件中有所定义
emacs@ubuntu:/usr/include$ grep dirent dirent.h | head -n 3
* POSIX Standard: 5.1.2 Directory Operations <dirent.h>
/* This file defines `struct dirent'.
#include <bits/dirent.h>
emacs@ubuntu:/usr/include$
从中可知是定义到了 bits/dirent.h 中
#ifndef _DIRENT_H
# error "Never use <bits/dirent.h> directly; include <dirent.h> instead."
#endif
struct dirent
{
#ifndef __USE_FILE_OFFSET64
__ino_t d_ino; //索引节点号, inode number
__off_t d_off; //在目录文件中的偏移,offset to this dirent
#else
__ino64_t d_ino;
__off64_t d_off;
#endif
unsigned short int d_reclen;
unsigned char d_type; //文件类型, the type of d_name
char d_name[256]; /* We must not include limits.h! */ //文件名,最长255字符 file name (null-terminated)
};
其中提供的信息也比较少,但是从中我们可以看出,文件名最大长度只能为255的原因 ,因为系统只给了256个字符的长度用来存储文件名
总体来看,这个目录条目结构体,也是一个简略的索引
stat
代码中有一个这样的定义
struct stat fs;
stat 是一种新的结构体
在头文件中有所定义
emacs@ubuntu:/usr/include$ grep stat sys/stat.h | grep include
#include <bits/stat.h>
emacs@ubuntu:/usr/include$
详细信息在 bits/stat.h 中
#ifndef _SYS_STAT_H
# error "Never include <bits/stat.h> directly; use <sys/stat.h> instead."
#endif
struct stat
{
__dev_t st_dev; /* Device. */ //文件使用的设备号
#if __WORDSIZE == 32
unsigned short int __pad1;
#endif
#if __WORDSIZE == 64 || !defined __USE_FILE_OFFSET64
__ino_t st_ino; /* File serial number. */ //索引节点号
#else
__ino_t __st_ino; /* 32bit file serial number. */
#endif //索引节点号
#else
#if __WORDSIZE == 32
__mode_t st_mode; /* File mode. */ //文件访问权限
__nlink_t st_nlink; /* Link count. */ //文件的硬连接数
#else
__nlink_t st_nlink; /* Link count. */ //文件的硬连接数
__mode_t st_mode; /* File mode. */ //文件访问权限
#endif
__uid_t st_uid; /* User ID of the file's owner. */ //所有者用户识别号
__gid_t st_gid; /* Group ID of the file's group.*/ //组识别号
#if __WORDSIZE == 64
int __pad0;
#endif
__dev_t st_rdev; /* Device number, if device. */
#if __WORDSIZE == 32
unsigned short int __pad2;
#endif
#if __WORDSIZE == 64 || !defined __USE_FILE_OFFSET64
__off_t st_size; /* Size of file, in bytes. */ //以字节为单位的文件容量
#else
__off64_t st_size; /* Size of file, in bytes. */ //以字节为单位的文件容量
#endif
__blksize_t st_blksize; /* Optimal block size for I/O. */
#if __WORDSIZE == 64 || !defined __USE_FILE_OFFSET64
__blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ //该文件所占的磁盘块
#else
__blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */
#endif
#ifdef __USE_MISC
/* Nanosecond resolution timestamps are stored in a format
equivalent to 'struct timespec'. This is the type used
whenever possible but the Unix namespace rules do not allow the
identifier 'timespec' to appear in the <sys/stat.h> header.
Therefore we have to handle the use of this header in strictly
standard-compliant sources special. */
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
#else
__time_t st_atime; /* Time of last access. */
unsigned long int st_atimensec; /* Nscecs of last access. */
__time_t st_mtime; /* Time of last modification. */
unsigned long int st_mtimensec; /* Nsecs of last modification. */
__time_t st_ctime; /* Time of last status change. */
unsigned long int st_ctimensec; /* Nsecs of last status change. */
#endif
#if __WORDSIZE == 64
long int __unused[3];
#else
# ifndef __USE_FILE_OFFSET64
unsigned long int __unused4;
unsigned long int __unused5;
# else
__ino64_t st_ino; /* File serial number. */
# endif
#endif
};
这里可以获得文件的绝大部分属性信息
Tip: 头文件里有很多如下形式的条件编译,是为了进行兼容处理
#ifndef
#else
#endif
读取文件属性过程
- 使用 opendir 函数打开一个目录文件(文件名字符串),返回一个 DIR 指针
- 使用 readdir 函数读取 DIR 指针,返回一个 dirent 指针
- 通过 stat 函数读取 direntP->d_name (文件名字符串),得到 stat 结构体
- 通过 stat 结构体,获取详细属性信息
有点小复杂
总结
以下这些函数可以应对绝大部分的文件或目录属性查询需求
- opendir
- closedir
- readdir
- rewinddir
- stat
通过各方面资料弄懂其参数的意义和返回值的类型,是熟练掌握的基础