不定期补充、修正、更新;欢迎大家讨论和指正
以下所用的函数只给出基本用法,想更进一步了解可以看看下面的博客或者查阅man手册
Linux系统编程_文件和系统属性
初始
先实现最简单的,利用stat函数获取单个文件信息,最终希望实现下面这种效果

int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
int fstatat(int dirfd, const char *pathname, struct stat *statbuf, int flags);
statbuf
struct stat {
dev_t st_dev; /* 块设备号 */
ino_t st_ino; /* Inode结点号 */
mode_t st_mode; /* 文件类型和文件权限 */
nlink_t st_nlink; /* 硬链接数 */
uid_t st_uid; /* 文件所属用户ID*/
gid_t st_gid; /* 文件所属组ID */
dev_t st_rdev; /* 字符设备ID */
off_t st_size; /* 文件大小 */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
struct timespec st_atim; /* 最后文件访问时间 */
struct timespec st_mtim; /* 最后修改文件内容时间 */
struct timespec st_ctim; /* 最后修改文件属性时间 */

效果如下,还十分简陋。
改进文件类型和权限部分
我们一步步改进,首先先改进文件类型和权限部分
目前显示的是33188,转换为二进制为1000000110100100,我们将其分为4组,1000/000/110/100/100
第一组文件类型,第二组代表设置位,就是SUID SGID, Sticky.后三组就是我们熟悉的所属用户,所属组,其他人的读写执行权限了。
这时我们就可以利用st_mode&0170000八进制(1111000000000000)提取前四位的信息,如果为0100000(1000 后面省略若干0)则为普通文件
好在<stat.h>将这些值宏定义,可以方便的调用,同理在权限部分也用这种方法,宏定义如下
S_IFMT 0170000 文件类型的位遮罩
S_IFSOCK 0140000 socket
S_IFLNK 0120000 符号链接(symbolic link)
S_IFREG 0100000 一般文件
S_IFBLK 0060000 区块装置(block device)
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符装置(character device)
S_IFIFO 0010000 先进先出(fifo)
S_ISUID 0004000 文件的(set user-id on execution)位
S_ISGID 0002000 文件的(set group-id on execution)位
S_ISVTX 0001000 文件的sticky位
S_IRWXU 00700 文件所有者的遮罩值(即所有权限值)
S_IRUSR 00400 文件所有者具可读取权限
S_IWUSR 00200 文件所有者具可写入权限
S_IXUSR 00100 文件所有者具可执行权限
S_IRWXG 00070 用户组的遮罩值(即所有权限值)
S_IRGRP 00040 用户组具可读取权限
S_IWGRP 00020 用户组具可写入权限
S_IXGRP 00010 用户组具可执行权限
S_IRWXO 00007 其他用户的遮罩值(即所有权限值)
S_IROTH 00004 其他用户具可读取权限
S_IWOTH 00002 其他用户具可写入权限
S_IXOTH 00001 其他用户具可执行权限
带参宏
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.)
成功改进文件类型的显示


文件权限的改进:这里通过移位操作来逻辑运算


改进用户名和组名
使用getpwuid和getgrgid函数即可实现
SYNOPSIS
struct passwd *getpwuid(uid_t uid);
struct passwd {
char *pw_name; /* 用户名 */
char *pw_passwd; /* 是否有密码 */
uid_t pw_uid; /* 用户ID */
gid_t pw_gid; /* 组ID */
char *pw_gecos; /* 注释*/
char *pw_dir; /* 用户家目录 */
char *pw_shell; /* shell程序 */
};
SYNOPSIS
struct group *getgrgid(gid_t gid);
struct group {
char *gr_name; /* 组名 */
char *gr_passwd; /* 是否有组密码 */
gid_t gr_gid; /* 组ID */
char **gr_mem; /* 指向组成员用户名的指针数组 */
};


改进时间
时间函数有许多种,这里用gmtime即可
struct tm *gmtime(const time_t *timep);
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
这里的时间是访问时间,ll出来的是创建时间,把函数内的atime改为ctime即可,这里就不更正了


改进符号链接文件
当文件类型是符号链接时,可以看到自己实现的ls有很大的错误,这是因为ll查看的是符号链接文件本身
而myls查看的是符号链接指向的文件属性
一部分错误只需将stat函数改为lstat函数即可

使用readlink函数读取符号链接文件指向背后的文件
剩下的就是排版问题,顺便把第一行的内容注释了。
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);


打印多条文件项
目前只能打印某一指定的文件路径,而ll的路径是目录时,会显示目录下所有的文件的信息
所以需要用opendir和readdir函数改进,当指定的路径时目录时,打印其下的所有文件,当指定路径为其他文件,正常输出。

opendir函数
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
readdir函数
struct dirent *readdir(DIR *dirp);
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 */
char d_name[256]; /* Null-terminated filename */
};


将打印单个文件内容的函数封装成display_file函数,在readdir调用
- 占坑
完整代码
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<dirent.h>
#include<errno.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
int main(int argc,char **argv){
struct stat statbuf={0};
if((lstat(argv[1],&statbuf))==-1){
perror("stat fail");
exit(1);
}
/*
printf("%d %ld %d %d %ld %ld %s\n",
statbuf.st_mode,
statbuf.st_nlink,
statbuf.st_uid,
statbuf.st_gid,
statbuf.st_size,
statbuf.st_atime,
argv[1]);
*/
switch (statbuf.st_mode & S_IFMT) {
case S_IFBLK: printf("b"); break;
case S_IFCHR: printf("c"); break;
case S_IFDIR: printf("d"); break;
case S_IFIFO: printf("p"); break;
case S_IFLNK: printf("l"); break;
case S_IFREG: printf("-"); break;
case S_IFSOCK: printf("s"); break;
default: printf("?"); break;
}
//打印文件类型
char f_mode[10]={0};
char tmp_buf[]="rwxrwxrwx";
for(int i=0;i<9;i++)
{
if(statbuf.st_mode & (1<<(8-i)))
f_mode[i]=tmp_buf[i];
else
f_mode[i]='-';
}
printf("%s ",f_mode);
//打印文件权限
printf("%ld ",statbuf.st_nlink);
//打印硬链接数
struct passwd *uid=getpwuid(statbuf.st_uid);
struct group *gid=getgrgid(statbuf.st_gid);
printf("%s %s\t",uid->pw_name,gid->gr_name);
//打印所有者和所属组
printf("%8ld ",statbuf.st_size);
struct tm *t=gmtime(&(statbuf.st_atime));
printf("%d月 %d %d:%d ",t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min);
//打印访问日期
if((statbuf.st_mode& S_IFMT)==S_IFLNK){
char sym_buf[50]={0};
readlink(argv[1],sym_buf,sizeof(sym_buf));
printf("%s -> %s",argv[1],sym_buf);
}
else
printf("%s",argv[1]);
//打印文件名
printf("\n");
return 0;
}
版权声明:本文为weixin_44568462原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。