最近学习了目录流,可从目录中获取该目录下各个文件的属性、文件类型、文件的权限等等内容。于是尝试在Linux下使用C语言模拟ls -l命令。
实现步骤:
一、打开目录
#include <sys/types.h>
#include <dirent.h>DIR *opendir(const char *name){
参数: 目录名
返回值: 成功返回一个目录流指针,失败返回NULL;
}
二、读取目录
#include <dirent.h>
struct dirent *readdir(DIR *dirp){
参数: dirp 目录流
返回值: 成功返回下一个目录项,失败或者读到目录末尾返回NULL,并设置错误号
}dirent结构体如下:
struct dirent {
ino_t d_ino; /* Inode number */ 索引节点编号
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 */ 以Null结尾的filenameType文件
char d_name[256]; /* Null-terminated filename */ 以Null结尾的文件名
};主要使用d_name;
三、获取文件属性
#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);
如果获取的文件是链接文件,那么获取的是符号链接文件的本身的相关属性返回值:成功返回0, 失败返回-1并设置错误号;
系统在stat.h中定义了相对应的带参宏,可以让我们快速判断文件类型
S_ISREG(m) 是否是一个常规文件
S_ISDIR(m) 是否是一个目录
S_ISCHR(m) 是否是一个字符设备
S_ISBLK(m) 是否是一个块设备
S_ISFIFO(m) 是否是一个FIFO文件
S_ISLNK(m) 是否是一个链接
S_ISSOCK(m) 是否是一个SOCKET文件
可使用 st_mode & 掩码 来得到 st_mode 中特定的部分
掩码的宏定义在 include <sys/stat.h>
#define S_IFMT 0170000 文件类型的位遮罩
st_mode & S_IFMT : 得到文件类型
struct stat statbuf;
/*获取文件属性*/
switch(statbuf.st_mode & S_IFMT){
case S_IFSOCK: printf("s"); break; /*套接字文件*/
case S_IFLNK: printf("l"); break; /*链接文件*/
case S_IFREG: printf("-"); break; /*普通文件*/
case S_IFBLK: printf("b"); break; /*块设备文件*/
case S_IFDIR: printf("d"); break; /*目录文件*/
case S_IFCHR: printf("c"); break; /*字符设备文件*/
case S_IFIFO: printf("p"); break; /*管道文件*/
}
struct stat这个结构体是用来描述一个linux系统文件系统中的文件属性的结构。
struct stat {
mode_t st_mode; //文件对应的模式,文件,目录等
ino_t st_ino; //inode节点号
dev_t st_dev; //设备号码
dev_t st_rdev; //特殊设备号码
nlink_t st_nlink; //文件的链接数
uid_t st_uid; //文件所有者
gid_t st_gid; //文件所有者对应的组
off_t st_size; //普通文件,对应的文件字节数
time_t st_atime; //文件最后被访问的时间
time_t st_mtime; //文件内容最后被修改的时间
time_t st_ctime; //文件状态改变时间
blksize_t st_blksize; //文件内容对应的块大小
blkcnt_t st_blocks; //伟建内容对应的块数量
};
四、获取文件权限
st_mode字段的低9位,代表文件的权限,标识了文件所有者(owner)、组用户(group)、其他用户(other)的读(r)、写(w)、执行(x)权限。
S_IRUSR 00400 owner has read permission 8 100 000 000
S_IWUSR 00200 owner has write permission 7 010 000 000
S_IXUSR 00100 owner has execute permission 6 001 000 000S_IRGRP 00040 group has read permission 5 000 100 000
S_IWGRP 00020 group has write permission 4 000 010 000
S_IXGRP 00010 group has execute permission 3 000 001 000S_IROTH 00004 others have read permission 2 000 000 100
S_IWOTH 00002 others have write permission 1 000 000 010
S_IXOTH 00001 others have execute permission 0 000 000 00100001-->00400 即1往左移8位
/*获取文件权限*/
for(int n = 8; n >= 0; n--){
if(statbuf.st_mode & (1 << n)){ //判断权限位的低9位 0-8
switch(n % 3){
case 2: printf("r"); break;
case 1: printf("w"); break;
case 0: printf("x"); break;
}
}else
printf("-");
五、获取硬连接数
通过访问stat结构体中的st_nlink来获取文件链接数
printf("%ld ", statbuf.st_nlink);
六、获取用户名和组名
使用getpwuid()函数
#include <sys/types.h>
#include <pwd.h>struct passwd *getpwuid(uid_t uid);
// 结构体struct passwd如下
struct passwd {
char *pw_name; /* username */
char *pw_passwd; / * user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
struct passwd *uidp = getpwuid(statbuf.st_uid);
struct passwd *gidp = getpwuid(statbuf.st_gid);
printf("%s %s " , uidp->pw_name, gidp->pw_name);
七、获取文件大小
通过访问stat结构体中的st_size来获取文件大小
printf("%ld ", statbuf.st_size); //获取文件大小
八、获取文件最后修改时间
通过访问stat结构体中的st_mtime来获取文件最后修改时间,调用localtime转换成月 日,时 分
struct tm *tp;
ctime(&statbuf.st_mtime); //获取文件最后修改时间
tp = localtime(&statbuf.st_mtime);
printf("%2d月 %2d %2d:%2d ",
tp->tm_mon + 1,
tp->tm_mday,
tp->tm_hour,
tp->tm_min);
九、获取文件名
通过访问dirent结构体中的d_name来获取文件名
printf("%s\n", dp->d_name); //文件名
十、关闭目录流
int closedir(DIR *dirp);
返回值: 成功返回0, 失败返回-1,并设置错误号;
实现代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <pwd.h>
#include <time.h>
int main(int argc, char *argv[])
{
if(argc != 2){
printf("please input %s & a pathname\n", argv[0]);
return -1;
}
DIR *dirp = opendir(argv[1]);
if(NULL == dirp){
perror("opendir");
return -1;
}
struct dirent *dp; //创建目录流指针
struct stat statbuf;
while(NULL != (dp = readdir(dirp))){
if(dp->d_name[0] == '.'){ //排除隐藏文件
continue;
}
else{
/*如果为链接文件,获取的是链接文件的本身的相关属性*/
if(-1 == lstat(dp->d_name, &statbuf)){
perror("lstat");
return -1;
}
/*获取文件属性*/
switch(statbuf.st_mode & S_IFMT){
case S_IFSOCK: printf("s"); break;
case S_IFLNK: printf("l"); break;
case S_IFREG: printf("-"); break;
case S_IFBLK: printf("b"); break;
case S_IFDIR: printf("d"); break;
case S_IFCHR: printf("c"); break;
case S_IFIFO: printf("p"); break;
}
/*获取文件权限*/
for(int n = 8; n >= 0; n--){
if(statbuf.st_mode & (1 << n)){
switch(n % 3){
case 2: printf("r"); break;
case 1: printf("w"); break;
case 0: printf("x"); break;
}
}else
printf("-");
}
printf("%2ld ", statbuf.st_nlink); //获取文件链接数
struct passwd *uidp = getpwuid(statbuf.st_uid);
struct passwd *gidp = getpwuid(statbuf.st_gid);
printf("%s %s " , uidp->pw_name, gidp->pw_name); //获取用户名、组名
printf("%5ld ", statbuf.st_size); //获取文件大小
struct tm *tp;
ctime(&statbuf.st_mtime); //获取文件最后修改时间
tp = localtime(&statbuf.st_mtime);
printf("%2d月 %2d %2d:%2d ",
tp->tm_mon + 1,
tp->tm_mday,
tp->tm_hour,
tp->tm_min);
}
printf("%s\n", dp->d_name); //文件名
}
closedir(dirp);
return 0;
}
实现效果: