- 前言:
初学Linux编程,相信很多朋友都和我一样,对于在Linux中如何获得文件相关属性,如文件大小、最后访问(读)时间、最后修改(写)时间、创建时间或最后更改(属性更改)时间等等存在着疑虑,花了点时间探究了下,发现其中的知识点挺多的,不是想象中的那么简单,于是写出来与大家分享一下。
Linux系统调用函数:stat、fstat、lstat
在Linux中,获得文件属性的系统调用函数有三个为stat、fstat、lstat,原型如下:
int stat(const char* file_name,struct stat* buf);
参数:
[in]file_name:文件(文件夹)的路径,相对路径和绝对路径都可以;
[out]buf:描述文件(文件夹)属性的结构体;
[return] 成功 0, 失败 -1。错误代码存在全局的errno中;
int fstat(int fd,struct stat* buf);
[in]fd:文件(文件夹)的标识符;
[out]buf:描述文件(文件夹)属性的结构体;
[return] 成功 0, 失败 -1。错误代码存在全局的errno中;
int lstat(const char* file_name,struct stat* buf);
[in]file_name:文件(文件夹)的路径,相对路径和绝对路径都可以;
[out]buf:描述文件(文件夹)属性的结构体;
[return] 成功 0, 失败 -1。错误代码存在全局的errno中;
lstat和stat的作用完全一样,区别在于如果传入lstat的路径为符号连接(“软连接”)时,获得的是符号连接本身的属性,而stat获得是符号连接目标文件(文件夹)的属性。
想要在程序中调用这三种方法,必须include的头文件有:
#include <sys/unistd.h>
#include <sys/stat.h>
结构体 stat
用man 2 stat,看下结构体stat的定义:
官方的注释已经很清楚了。关于文件(文件夹)属性的信息都存在这里面的,下面就利用这个结构体,举例来说能做的一些应用。
- 应用一:判断文件(文件夹)是否可读、可写、可执行:
判断文件(文件夹)是否可读的函数:
函数的过程很简单,判断逻辑在注释中也写的很清楚了,需要包含的头文件:
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
利用这个思路,判断可写,判断可运行的函数就很容易写出了。
下面是判断文件(文件夹)是否可写的函数:
下面是判断文件(文件夹)是否可运行的函数:
- 应用二:获得文件(文件夹)的大小
对于普通文件来说,获取文件占用的大小很简单,只需要返回结构体stat的st_sizee即可。但是对于文件夹来说,结构体stat的st_size表明的是文件夹本身占用的空间大小(在Linux文件体系中,对于文件夹来说是需要空间来存储自身文件夹下的文件或文件夹的inode号的),与我们普遍意义上理解的文件夹应该返回的是其包含文件或文件夹的总容量不同,因此需要设计一个函数来获得文件夹下所有文件(文件夹)的总容量:
其实更加通用的遍历目录函数可以这样设计:用注册回调函数的方法来实现,这个回调函数的参数就是每个遍历项的路径(最好是绝对路径),那么以后遍历目录就不需要改变了 只需要在应用中注册不同的回调函数就可以了。实现如下:
这种实现方式的优势是利用回调函数,遍历文件夹的操作可以复用,缺点是如果需要统计每次回调函数的结果就需要额外的一个全局参数(当然可以用命名空间的方式局部化。。。)。利用这种方式,还能方便的实现出统计文件夹下各种文件类型的数量,属于某个用户ID文件的数量等等(改改两个回调函数就行了)。
- 应用三:获得文件(文件夹)的三个时间:最后访问(读)时间、最后修改(写)时间、创建时间或最后更改(属性更改)时间
在项目中,我们经常会需要获得文件(文件夹)的最后访问(读)时间、最后修改(写)时间、创建时间或最后更改(属性更改)时间这三种时间,在Linux中,触发这三种时间改变的条件分别是:
最后访问(读)时间:文件(文件夹)最后一次被存取或执行的时间;
最后修改(写)时间:文件(文件夹)最后一次被修改的时间,这里指的修改是内容上的;
创建时间或最后更改(属性更改)时间:文件(文件夹)最后一次被更改的时间,这里指的修改是属性上的,如所有者、权限等;
对应到结构体stat上就是:
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
值得一提的是,以上三种时间在Linux中是用UTC表示的,单位是秒,举个例子:1285328411表示的是从1970年1月1日开始所经过的秒数,值得注意的是这里的时间是UTC时间。
这里仅用最后访问(读)时间为例:
另外两种时间的获取方式,就当作小练习吧。
- 应用四:获得文件类型
最后来谈谈如何根据st_mode来判断文件(文件夹)的类型,这里可以利用库本身就定义好的一些宏:
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) /**文件夹的判断*/
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /**管道文件的判断*/
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) /**字符设备的判断*/
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) /**块设备的判断*/
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) /**普通文件的判断*/
实例如下:
当然在项目中一般是不用硬编码的,可以定义相关的enum。
***********************************************************************************************************************************************************
- 小结
万事开头难,一开始学linux编程,确实很难摸到门径,但是我相信只要找到合适的方法和不懈的努力,应该能学好的!自己给自己加油!
仓促之作,一定会漏洞百出,请大侠们多多批评指教。。。哈哈,拜谢了!!!
所有实例均在gcc 4.1.2,redhat 企业版 5.3下编译测试通过,环境参数如下:
Using built-in specs.
Target: i386-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=i386-redhat-linux
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)