Linux 檔案目錄資訊列印stat+dirent
stat的基本使用:
所需標頭檔案: #include<sys/types.h>,#include<sys/stat.h>,#include<unistd.h>
函式原型:
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
功能:得到檔案的資訊,將其儲存在buf結構中;
引數:
對於stat() ,lstat()來說const char *path,是要檢視屬性的檔案或目錄的全路徑名稱
對於fstat來說int fd,為已開啟的檔案描述詞
struct _stat *buffer:結構體物件地址
返回值:
成功返回 1,失敗返回 0
struc stat 詳解:
struct stat { dev_t st_dev; /* ID of device containing file -檔案所在裝置的ID*/ ino_t st_ino; /* inode number -inode節點號*/ mode_t st_mode; /* protection -保護模式?*/ nlink_t st_nlink; /* number of hard links -鏈向此檔案的連線數(硬連線)*/ uid_t st_uid; /* user ID of owner -user id*/ gid_t st_gid; /* group ID of owner - group id*/ dev_t st_rdev; /* device ID (if special file) -裝置號,針對裝置檔案*/ off_t st_size; /* total size, in bytes -檔案大小,位元組為單位*/ blksize_t st_blksize; /* blocksize for filesystem I/O -系統塊的大小*/ blkcnt_t st_blocks; /* number of blocks allocated -檔案所佔塊數*/ 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 - */ };
st_mode欄位:
st_mode 一共有16位,主要包含了 3 部分資訊:
- 15-12 位儲存檔案型別
- 11-9 位儲存執行檔案時設定的資訊
- 8-0 位儲存檔案訪問許可權
圖片來自網路
r=4 表示可讀
w=2表示可寫
x=1表示可執行
st_mode 的巨集描述作用:
S_IFMT 0170000 檔案型別的位遮罩 S_IFSOCK 0140000 scoket S_IFLNK 0120000 符號連線 S_IFREG 0100000 一般檔案 S_IFBLK 0060000 區塊裝置 S_IFDIR 0040000 目錄 S_IFCHR 0020000 字元裝置 S_IFIFO 0010000 先進先出 S_ISUID 04000 檔案的(set user-id on execution)位 S_ISGID 02000 檔案的(set group-id on execution)位 S_ISVTX 01000 檔案的sticky位 S_IRUSR(S_IREAD) 00400 檔案所有者具可讀取許可權 S_IWUSR(S_IWRITE)00200 檔案所有者具可寫入許可權 S_IXUSR(S_IEXEC) 00100 檔案所有者具可執行許可權 S_IRGRP 00040 使用者組具可讀取許可權 S_IWGRP 00020 使用者組具可寫入許可權 S_IXGRP 00010 使用者組具可執行許可權 S_IROTH 00004 其他使用者具可讀取許可權 S_IWOTH 00002 其他使用者具可寫入許可權 S_IXOTH 00001 其他使用者具可執行許可權
POSIX定義了下面幾種通過st_mode判斷檔案型別的巨集:
The following POSIX macros are defined to check the file type using the st_mode field:
S_ISREG(m) /* is it a regular file? -普通檔案 */
S_ISDIR(m) /* directory? -目錄檔案? */
S_ISCHR(m) /* character device? -字元裝置檔案? */
S_ISBLK(m) /* block device? -塊裝置檔案? */
S_ISFIFO(m) /* FIFO (named pipe)? -管道檔案? */
S_ISLNK(m) /* symbolic link? (Not in POSIX.1-1996.) -符號連結? */
S_ISSOCK(m) /* socket? (Not in POSIX.1-1996.) -套介面? */
DIR檔案目錄操作:
常用函式:
DIR* opendir (const char * path );
struct dirent *readdir(DIR *dp);
void rewinddir(DIR *dp);
int closedir(DIR *dp);
long telldir(DIR *dp);
void seekdir(DIR *dp,long loc);
所需標頭檔案:#include<sys/types.h> #include<dirent.h>
函式:DIR* opendir (const char * path );
功能:開啟一個目錄,在失敗的時候返回一個空的指標。
獲取path子目錄下的所由檔案和目錄的列表,如果path是個檔案則返回值為NULL。
返回值(DIR): DIR 結構體的原型為:struct_dirstream
引數:const char * path 檔案路徑相對或者絕對
函式: struct dirent* readdir(DIR* dir_handle);
功能:讀取opendir 返回值的那個列表,類似於read檔案讀取
返回值: dirent的結構型別
引數:DIR
函式:void rewinddir(DIR *dp);
功能:用來設定引數dir目錄流目前的讀取位置為原來開頭的讀取位置,沒有返回值的函式
返回值:void
引數:DIR
函式:int closedir(DIR *dp);
功能:關閉引數dir所指的目錄流。關閉成功則返回0,失敗返回-1,錯誤原因存於errno 中。EBADF 引數dir為無效的目錄流。
注意:目錄檔案作為一種檔案,再開啟必須關閉,否則會由於檔案的程序開啟檔案過多而不能開啟新的檔案。因此opendir函式和closedir函式同樣是配對出現的。
返回值:關閉成功則返回0,失敗返回-1
引數:DIR
函式:long telldir(DIR *dp);
功能:telldir()函式的返回值記錄著一個目錄流的當前位置。此返回值代表距離目錄檔案開頭的偏移量返回值返回下個讀取位置,有錯誤發生時返回-1。你可以在隨後的seekdir函式呼叫中利用這個值來重置目錄掃描到當前位置。錯誤程式碼 EBADF引數dir為無效的目錄流。
返回值:有錯誤發生時返回-1。
引數:DIR
函式:void seekdir(DIR *dp,long loc);
功能:seekdir()用來設定引數dir目錄流當前的讀取位置,在呼叫readdir()時便從此新位置開始讀取。引數offset 代表距離目錄檔案開頭的偏移量。
返回值:錯誤程式碼 EBADF 引數dir為無效的目錄流
引數:DIR 引數offset 代表距離目錄檔案開頭的偏移量。
DIR結構體:
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;
相當於目錄描述符用於存放目錄下的子目錄類似於檔案描述符
struct dirent 結構體:
struct dirent
{
long d_ino; /* inode number 索引節點號 */
off_t d_off; /* offset to this dirent 在目錄檔案中的偏移 */
unsigned short d_reclen; /* length of this d_name 檔名長 */
unsigned char d_type; /* the type of d_name 檔案型別 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 檔名,最長255字元 */
}
LINUX系統下的一個頭檔案,在這個目錄下/usr/include為了獲取某資料夾目錄內容,所使用的結構體。
所需要的知識已經介紹完了接下來介紹怎樣列印一個目錄下的所有子目錄的屬性
這裡直接使用遞迴操作,但是由於d_name是指向的是目錄的名字不是路徑所以只需要把檔案路徑儲存下來進行遞迴操作就好,有值得注意的一點是當目錄名稱是"."或".."時不需要往下讀去因為這樣會進入死迴圈的。
程式碼:
標頭檔案:head.h
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <math.h>
#include <dirent.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#define MAX_N 1024
#define MAX_M 100
extern char get_type(mode_t mod);
extern void get_perm(mode_t mod,char *buf);
extern void get_ltime(time_t *t,char *buf);
extern void put_stat(char *dir,char *name);
extern void get_dir(char *dir);
操作函式:oper.c
#include"./head.h"
//獲取檔案型別
char get_type(mode_t mod)
{
/*
if( S_ISREG(mod) ) return '-';
if( S_ISDIR(mod) ) return 'd';
if( S_ISCHR(mod) ) return 'c';
if( S_ISBLK(mod) ) return 'b';
if( S_ISLNK(mod) ) return 'l';
if( S_ISSOCK(mod) ) return 's';
if( S_ISFIFO(mod) ) return 'p';
*/
switch(mod & S_IFMT)
{
case S_IFSOCK: return 's';//socket 套接字
case S_IFREG: return '-';//普通檔案
case S_IFCHR: return 'c';//字元裝置
case S_IFBLK: return 'b';//塊裝置
case S_IFLNK: return 'l';//符號連結
case S_IFIFO: return 'p';//管道檔案
case S_IFDIR: return 'd';//目錄檔案
}
}
//獲取檔案許可權
void get_perm(mode_t mod,char *buf)
{
bzero(buf,sizeof(buf));
int i = 9;
while(i--)
{
if(mod & 1<<i)
{
switch((8-i)%3)
{
case 0: buf[8-i] = 'r'; break;
case 1: buf[8-i] = 'w'; break;
case 2: buf[8-i] = 'x'; break;
}
}
else
buf[8-i] = '-';
}
}
//獲取檔案最後一次修改時間
void get_ltime(time_t *t,char *buf)
{
struct tm *tmp;
tmp=localtime(t);
sprintf(buf,"%d-%d-%d %d:%d:%d", (1900 + tmp->tm_year), ( 1 + tmp->tm_mon), tmp->tm_mday,
tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
return ;
}
void put_stat(char *dir,char *name)
{
struct stat s;
if(-1 == stat(dir, &s) )
{
perror("stat");
exit(0);
}
char buf_perm[MAX_M];
get_perm(s.st_mode,buf_perm);//獲取檔案許可權
char buf_time[MAX_M];
get_ltime(&s.st_ctime,buf_time);//獲取檔案最後一次修改時間
printf( "%c%s %5d %15s %15s %15ld %15s %15s \033[0m\n",
get_type(s.st_mode),
buf_perm,
s.st_nlink,
getpwuid(s.st_uid)->pw_name,
getgrgid(s.st_gid)->gr_name,
s.st_size,
buf_time,
name );
if(get_type(s.st_mode)=='d'&&strcmp(name,".")!=0&&strcmp(name,"..")!=0)//如果是一個目錄就繼續往下讀
{
get_dir(dir);
}
return ;
}
void get_dir(char *dir)//讀取檔案
{
DIR *dp = opendir(dir); //開啟目錄的子目錄
struct dirent *p;
printf("%s:\n",dir);
puts("{");
while( p = readdir(dp) )//讀取目錄下的檔案資訊
{
char ttp[MAX_N];
bzero(ttp,sizeof(ttp));
strcat(ttp,dir);
if(strcmp(ttp,"/")!=0)strcat(ttp,"/");//如果是根目錄不需要加
strcat(ttp,p->d_name);
put_stat(ttp,p->d_name);
}
puts("}");
return;
}
主函式:main.c
#include"./head.h"
int main(int argc,char *argv[])
{
if(argc<2) {perror("argc is not enough\n");return 0;}
get_dir(argv[1]);
return 0;
}
Makefile (區域性萬能)
SRC=$(wildcard *.c)
OBJ=$(patsubst %.c ,%.o,${SRC})
all=main
${all}:${OBJ}
gcc -g -o [email protected] $^
.PHONY:
clean:
rm -rf *.o main
執行截圖