- #include <sys/stat.h>
- int stat (const char *restrict pathname,struct stat* restrict buf)
- int fstat(int filedes,struct stat *buf);
- int lstat(const char *restrict pathname,struct stat *restrict buf);
stat, fstat and lstat是用來檢查文件屬性的。他們將文件屬性信息通過a struct stat object 返回。
int stat (const char *filename, struct stat *buf) [Function]
The stat function returns information about the attributes of the file named by
filename in the structure pointed to by buf.
If filename is the name of a symbolic link, the attributes you get describe the file
that the link points to. If the link points to a nonexistent file name, then stat fails
reporting a nonexistent file.
The return value is 0 if the operation is successful, or -1 on failure. In addition to the
usual file name errors (see Section 11.2.3 [File Name Errors], page 224, the following
errno error conditions are defined for this function:
ENOENT The file named by filename doesn’t exist.
When the sources are compiled with _FILE_OFFSET_BITS == 64 this function is in fact
stat64 since the LFS interface transparently replaces the normal implementation.
int fstat (int filedes, struct stat *buf) [Function]
The fstat function is like stat, except that it takes an open file descriptor as an
argument instead of a file name. See Chapter 13 [Low-Level Input/Output], page 296.
Like stat, fstat returns 0 on success and -1 on failure. The following errno error
conditions are defined for fstat:
EBADF The filedes argument is not a valid file descriptor.
When the sources are compiled with _FILE_OFFSET_BITS == 64 this function is in fact
fstat64 since the LFS interface transparently replaces the normal implementation.
int lstat (const char *filename, struct stat *buf) [Function]
The lstat function is like stat, except that it does not follow symbolic links. If
filename is the name of a symbolic link, lstat returns information about the link
itself; otherwise lstat works like stat. See Section 14.5 [Symbolic Links], page 357.
When the sources are compiled with _FILE_OFFSET_BITS == 64 this function is in fact
lstat64 since the LFS interface transparently replaces the normal implementation.
一旦給出pathname,stat函數就返回與此命名有關的信息結構,fstat函數獲取已在描述符filedes上打開文件的有關信息。lstat函數類似於stat,但是當命名文件時一個符號鏈接,lstat返回該符號鏈接的有關信息。而不是有該符號引用文的信息。
第二個參數是buf,指針。指向我們必須提供的結構。
- 應用一:判斷文件(文件夾)是否可讀、可寫、可執行:
判斷文件(文件夾)是否可讀的函數:
- #include <sys/unistd.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- /** /brief 判斷文件(文件夾)在當前上下文環境下是否可讀
- *
- * /param const char* _path: 文件或文件夾的路徑,可以為絕對路徑或相對路徑
- * /return signed char
- * 1:可讀;
- * 0:不可讀;
- * -1:錯誤,錯誤號可以從全局的errno獲取;
- */
- signed char canRead(constchar* _path)
- {
- struct stat buff;
- if(stat(_path,&buff) == 0)
- {
- /**當前用戶為root,當然擁有讀的權限*/
- if(0 == geteuid())
- {
- return 1;
- }
- /**當前用戶為該文件(文件夾)的所有者,判斷是否有所有者可讀權限*/
- else if(buff.st_uid == geteuid())
- {
- return ((buff.st_mode & S_IRUSR != 0)?1 : 0);
- }
- /**當前用戶組為該文件(文件夾)的用戶組,判斷是否有用戶組可讀權限*/
- else if(buff.st_gid == getegid())
- {
- return ((buff.st_mode & S_IRGRP != 0)?1 : 0);
- }
- /**判斷其他人是否有可讀權限*/
- else
- {
- return ((buff.st_mode & S_IROTH != 0)?1 : 0);
- }
- }
- else
- {
- return -1;
- }
- }
函數的過程很簡單,判斷邏輯在注釋中也寫的很清楚了,需要包含的頭文件:
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
利用這個思路,判斷可寫,判斷可運行的函數就很容易寫出了。
下面是判斷文件(文件夾)是否可寫的函數:
- #include <sys/unistd.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- /** /brief 判斷文件(文件夾)在當前上下文環境下是否可寫
- *
- * /param const char* _path: 文件或文件夾的路徑,可以為絕對路徑或相對路徑
- * /return signed char
- * 1:可讀;
- * 0:不可讀;
- * -1:錯誤,錯誤號可以從全局的errno獲取;
- */
- signed char canWrite(constchar* _path)
- {
- struct stat buff;
- if(stat(_path,&buff) == 0)
- {
- /**當前用戶為root,當然擁有寫的權限*/
- if(0 == geteuid())
- {
- return 1;
- }
- /**當前用戶為該文件(文件夾)的所有者,判斷是否有所有者可寫權限*/
- else if(buff.st_uid == geteuid())
- {
- return ((buff.st_mode & S_IWUSR != 0)?1 : 0);
- }
- /**當前用戶組為該文件(文件夾)的用戶組,判斷是否有用戶組可寫權限*/
- else if(buff.st_gid == getegid())
- {
- return ((buff.st_mode & S_IWGRP != 0)?1 : 0);
- }
- /**判斷其他人是否有可讀權限*/
- else
- {
- return ((buff.st_mode & S_IWOTH != 0)?1 : 0);
- }
- }
- else
- {
- return -1;
- }
- }
下面是判斷文件(文件夾)是否可運行的函數:
- #include <sys/unistd.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- /** /brief 判斷文件(文件夾)在當前上下文環境下是否可執行
- *
- * /param const char* _path: 文件或文件夾的路徑,可以為絕對路徑或相對路徑
- * /return signed char
- * 1:可讀;
- * 0:不可讀;
- * -1:錯誤,錯誤號可以從全局的errno獲取;
- */
- signed char canExecute(constchar* _path)
- {
- struct stat buff;
- if(stat(_path,&buff) == 0)
- {
- /**當前用戶為root,當然擁有讀的權限*/
- if(0 == geteuid())
- {
- return 1;
- }
- /**當前用戶為該文件(文件夾)的所有者,判斷是否有所有者可執行權限*/
- else if(buff.st_uid == geteuid())
- {
- return ((buff.st_mode & S_IXUSR != 0)?1 : 0);
- }
- /**當前用戶組為該文件(文件夾)的用戶組,判斷是否有用戶組可執行權限*/
- else if(buff.st_gid == getegid())
- {
- return ((buff.st_mode & S_IXGRP != 0)?1 : 0);
- }
- /**判斷其他人是否有可執行權限*/
- else
- {
- return ((buff.st_mode & S_IXOTH != 0)?1 : 0);
- }
- }
- else
- {
- return -1;
- }
- }
- 應用二:獲得文件(文件夾)的大小
對於普通文件來說,獲取文件占用的大小很簡單,只需要返回結構體stat的st_sizee即可。但是對於文件夾來說,結構體stat的st_size表明的是文件夾本身占用的空間大小(在Linux文件體系中,對於文件夾來說是需要空間來存儲自身文件夾下的文件或文件夾的inode號的),與我們普遍意義上理解的文件夾應該返回的是其包含文件或文件夾的總容量不同,因此需要設計一個函數來獲得文件夾下所有文件(文件夾)的總容量:
- #include <sys/unistd.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <dirent.h>
- #include <string>
- /** /brief 獲得文件夾的總大小
- *
- * /param const char* _path: 文件夾的路徑,可以為絕對路徑或相對路徑
- * /return off_t
- * 返回路徑指向文件夾的總容量;
- */
- off_t getDirTotalSize(constchar* _path)
- {
- struct dirent* ent(0);
- DIR* pDir(opendir(_path));
- off_t result(0);
- char buff[512] = {0};
- while ((ent = readdir(pDir)) != 0)
- {
- /**在Linux文件系統中 .和..也是特殊的子目錄,明顯這里不應該計算*/
- if(strcmp(ent->d_name,".") == 0 || strcmp(ent->d_name,"..") == 0)
- {
- continue;
- }
- sprintf(buff, "%s/%s", _path, ent->d_name);
- /**如果當前是目錄 則遞歸計算子目錄的大小*/
- if (ent->d_type == DT_DIR)
- {
- result += getDirTotalSize(buff);
- }
- else
- {
- result += getFileSize(buff);
- }
- }
- return result;
- }
- /** /brief 獲得文件的大小
- *
- * /param const char* _path: 文件的路徑,可以為絕對路徑或相對路徑
- * /return off_t
- * 成功則返回路徑指向文件的大小;
- * -1:錯誤,錯誤號可以從全局的errno獲取;
- */
- off_t getFileSize(const char* _path)
- {
- struct stat buff;
- if (stat(_path, &buff) == 0)
- {
- return buff.st_size;
- }
- else
- {
- return -1;
- }
- }
其實更加通用的遍歷目錄函數可以這樣設計:用注冊回調函數的方法來實現,這個回調函數的參數就是每個遍歷項的路徑(最好是絕對路徑),那么以后遍歷目錄就不需要改變了 只需要在應用中注冊不同的回調函數就可以了。實現如下:
- #include <sys/unistd.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <dirent.h>
- #include <string>
- #include <stdio.h>
- off_t getFileSize(const char* _path);
- void traverseDir(constchar* _path,off_t(*_callPtr)(constchar*),void(*_callbackResPtr)(off_t) = 0);
- void sumSize(off_t _size);
- /**< 計算的文件夾大小結果 */
- off_t result(0);
- int main(int argc,char** argv)
- {
- traverseDir(*(++argv),getFileSize,sumSize);
- printf("%ld", result);
- return 0;
- }
- /** /brief 遞歸遍歷目錄,並在遇到非文件夾時
- * 調用回調函數off_t(*_callPtr)(const char*) 參數為當前的絕對路徑
- *
- * /param const char* _path: 需要遍歷的文件夾的路徑,可以為絕對路徑或相對路徑
- * /param off_t(*_callPtr)(const char*):
- * 需要遍歷的文件夾的路徑,可以為絕對路徑或相對路徑
- * /param void(*_callbackResPtr)(off_t):
- * 以每次調用完_callPtr后的返回值為參數的回調函數,默認值為0,
- * 表示不對每次調用_callPtr的結果感興趣
- * /return void
- */
- void traverseDir(constchar* _path,off_t(*_callPtr)(constchar*),void(*_callbackResPtr)(off_t))
- {
- struct dirent* ent(0);
- DIR* pDir(opendir(_path));
- char buff[512] = {0};
- while ((ent = readdir(pDir)) != 0)
- {
- /**在Linux文件系統中 .和..也是特殊的子目錄,明顯這里不應該遞歸*/
- if(strcmp(ent->d_name,".") == 0 || strcmp(ent->d_name,"..") == 0)
- {
- continue;
- }
- sprintf(buff, "%s/%s", _path, ent->d_name);
- /**如果當前是目錄 則遞歸子目錄*/
- if (ent->d_type == DT_DIR)
- {
- traverseDir(buff,_callPtr,_callbackResPtr);
- }
- else
- {
- if(_callbackResPtr)
- {
- (*_callbackResPtr)( (*_callPtr)(buff) );
- }
- else
- {
- (*_callPtr)(buff);
- }
- }
- }
- return;
- }
- /** /brief 獲得文件的大小
- *
- * /param const char* _path: 文件的路徑,可以為絕對路徑或相對路徑
- * /return off_t
- * 成功則返回路徑指向文件的大小;
- * -1:錯誤,錯誤號可以從全局的errno獲取;
- */
- off_t getFileSize(const char* _path)
- {
- struct stat buff;
- if (stat(_path, &buff) == 0)
- {
- return buff.st_size;
- }
- else
- {
- return -1;
- }
- }
- /** /brief 一個簡單的統計,把每次傳入的數值累加起來 賦值到result上
- *
- * /param off_t _size: 文件的大小
- * /return void
- */
- void sumSize(off_t _size)
- {
- result += _size;
- return;
- }
這種實現方式的優勢是利用回調函數,遍歷文件夾的操作可以復用,缺點是如果需要統計每次回調函數的結果就需要額外的一個全局參數(當然可以用命名空間的方式局部化。。。)。利用這種方式,還能方便的實現出統計文件夾下各種文件類型的數量,屬於某個用戶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時間。
這里僅用最后訪問(讀)時間為例:
- #include <sys/unistd.h>
- #include <sys/stat.h>
- /** /brief 判斷文件(文件夾)的最后訪問時間
- *
- * /param const char* _path: 文件或文件夾的路徑,可以為絕對路徑或相對路徑
- * /return time_t
- * >0:成功;
- * 0:錯誤;
- */
- time_t getReadTime(constchar* _path)
- {
- struct stat buff;
- if(stat(_path,&buff) == 0)
- {
- return buff.st_atime;
- }
- return 0;
- }
另外兩種時間的獲取方式,就當作小練習吧。
- 應用四:獲得文件類型
最后來談談如何根據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) /**普通文件的判斷*/
實例如下:
- #include <sys/unistd.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- /** /brief 判斷文件(文件夾)的類型
- *
- * /param const char* _path: 文件或文件夾的路徑,可以為絕對路徑或相對路徑
- * /return signed char
- * 0:普通文件
- * 1:文件夾
- * 2:管道文件
- * 3:字符設備文件
- * 4:塊設備文件
- * -1:錯誤,錯誤號可以從全局的errno獲取;
- */
- signed char getFileType(constchar* _path)
- {
- struct stat buff;
- if(stat(_path,&buff) == 0)
- {
- if(S_ISREG(buff.st_mode))
- {
- return 0;
- }
- else if(S_ISDIR(buff.st_mode))
- {
- return 1;
- }
- else if(S_ISFIFO(buff.st_mode))
- {
- return 2;
- }
- else if(S_ISCHR(buff.st_mode))
- {
- return 3;
- }
- else if(S_ISBLK(buff.st_mode))
- {
- return 4;
- }
- else
- {
- return -1;
- }
- }
- else
- {
- return -1;
- }
- }
當然在項目中一般是不用硬編碼的,可以定義相關的enum。