模拟实现 linux 的命令 ls -l
首先要直到所输出的内容都代表着什么。
分别对应
文件类型及权限 硬链接数 拥有者 拥有者所在组 文件大小 月 日 时间 文件名
我们可以从前向后一步一步进行处理。
在 linux 中,我们可以利用 stat()函数来获取一个文件的状态。
#include <sys/stat.h>
#include <unistd.h>
int stat(const char* file_name, struct stat*buf);
//成功返回 0 ,失败返回 -1
// 取得的文件状态存放在 buf 指针指向的 stat 结构体中
stat 结构体的定义如下
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B 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 用来判断文件类型及权限
1.首先处理文件类型及权限
首先要清楚 Linux 下7种文件的类型
d 目录文件
- 普通文件
p 管道文件
l 软链接文件
b 块设备文件
c 字符设备文件
s socket 套接文件
这些文件类型信息都包含在stat结构的 st_mode 成员中。
S_IFMT 0170000 文件类型隐码
S_IFSOCK 0140000 socket 文件
S_IFLNK 0120000 软链接文件
S_IFREG 0100000 一般文件
S_IFBLK 0060000 块设备文件
S_IFDIR 0040000 目录文件
S_IFCHR 0020000 字符文件
S_IFIFO 0010000 管道文件
S_ISUID 0004000 UID
S_ISGID 0002000 GID
S_ISVTX 0001000 用户设置为
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 其他用户执行权限
为了判断文件的类型,我们可以将所要读取文件的 st_mode 与 文件类型的隐码按位与,然后依次进行比较。也可以使用库中所提供的宏来获得文件类型(man 2 stat 中查看)
在这里我使用第一种方法
void type(mode_t mode){
switch (mode & S_IFMT){
ase S_IFSOCK : printf("socket\n");break;
case S_IFLNK : printf("symoblic link\n");break;
case S_IFREG : printf("regualr file\n");break;
case S_IFBLK : printf("blcok file\n");break;
case S_IFDIR : printf("director file\n");break;
case S_IFCHR : printf("character file\n");break;
case S_IFIFO : printf("FIFO file\n");break;
}
}
int main( int argc, char* argv[] )
{
if(argc != 2){
fprintf(stderr,"usage:%s filename\n",argv[0]);
exit(0);
}
struct stat sbuf;
if(stat(argv[1], &sbuf) == -1)
perror("stat"),exit(1);
}
同样的,文件权限同文件类型一样,将 mode_t 与不同的隐码按位与,之后与权限位进行比较。
2.处理文件的硬链接数
硬链接数直接保存在 struct stat 结构体中的 st_nlink 中,所以我们可以直接获取并输出。
3.处理文件的大小
- lseek() 函数
off_t lseek(int fd, off_t offset, int whence);
whence:
#define SEEK_SET 0 //文件开头
#define SEEK_CUR 1 //当前位置
#define SEEK_END 2 //文件末尾
此函数如果执行成功将返回当前读写位置距离文件头部的字节数
所以我们可以将读写位置移动到文件末尾,并保存其返回值,这个返回值就是文件的大小。
stat
stuct stat 结构体中的 st_size 中保存着本文件的大小,可以直接进行读取。4.拥有者与拥有者所在组
在 /etc/passwd/ 和 /etc/group/ 下分别保存着对应 id 的拥着者及所在组,我们可以将这两个文件遍历并分别与 struct stat 结构体中的 st_uid 和 st_gid 进行比较获得。在这里提供一个更快捷的方法。
获取拥有者
getpwuid() 函数
是通过用户的uid查找用户的passwd数据
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; /* real name */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
获取所在组
getgrgid() 函数
struct group *getgrgid(gid_t gid);
其中 struct group 的结构为
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* group members */
};
而其中的参数 uid 和 gid 都可以从 struct stat 结构体中获得到。
5.时间
从输出结果来看,我们可以发现时间顺序为 月 - 日 - 时间
我们需要一些时间相关的函数
time()函数
time_t time(time_t *t);
返回从 1970年1月1日 0时0分0秒到现在的时间---秒数
这样的秒数我们可以手动去转换,库中提供了 localtime()函数来为我们将时间进行转换。
localtime() 函数
struct tm *localtime(const time_t *timep);
其中 struct tm 的结构为
struct tm {
int tm_sec; /* seconds */
int tm_min; /* minutes */
int tm_hour; /* hours */
int tm_mday; /* day of the month */
int tm_mon; /* month */
int tm_year; /* year */
int tm_wday; /* day of the week */
int tm_yday; /* day in the year */
int tm_isdst; /* daylight saving time */
};
6.文件名
文件名即我们所输入的 argv[1]
接下来我们就可以对整个程序进行编写
实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <string.h>
#include <time.h>
void type(char * buf,mode_t mode){ // 文件类型及权限
switch (mode & S_IFMT){ //类型
case S_IFSOCK : buf[0] = 's';break;
case S_IFLNK : buf[0] = 'l';break;
case S_IFREG : buf[0] = '-';break;
case S_IFBLK : buf[0] = 'b';break;
case S_IFDIR : buf[0] = 'd';break;
case S_IFCHR : buf[0] = 'c';break;
case S_IFIFO : buf[0] = 'p';break;
}
int s_mode = mode & S_IRWXU; // 拥有者权限
if(s_mode & S_IRUSR)
buf[1] = 'r';
if(s_mode & S_IWUSR)
buf[2] = 'w';
if(s_mode & S_IXUSR)
buf[3] = 'x';
s_mode = mode & S_IRWXG; //同组者权限
if(s_mode & S_IRGRP)
buf[4] = 'r';
if(s_mode & S_IWGRP)
buf[5] = 'w';
if(s_mode & S_IXGRP)
buf[6] = 'x';
s_mode = mode & S_IRWXO; // 其他者权限
if(s_mode & S_IROTH)
buf[7] = 'r';
if(s_mode & S_IWOTH)
buf[8] = 'w';
if(s_mode & S_IXOTH)
buf[9] = 'x';
buf[10] = '.';
}
int main( int argc, char* argv[] )
{
if(argc != 2){
fprintf(stderr,"usage:%s filename\n",argv[0]);
exit(0);
}
struct stat sbuf;
if(stat(argv[1], &sbuf) == -1) // 获取文件状态
perror("stat"),exit(1);
char buf[12] = "?---------";
type(buf,sbuf.st_mode);
char buf1[20] = {0};
char buf2[20] = {0};
struct passwd * my_uinfo;
my_uinfo = getpwuid(sbuf.st_uid); // 拥有者名
struct group * my_ginfo;
my_ginfo = getgrgid(sbuf.st_gid); // 所在组名
time_t tp;
tp = time(NULL);
struct tm* clock;
clock = localtime(&tp); //获取时间
char* daybuf[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
printf("%s %d %s %s %d %s %d %d:%d\n",buf, sbuf.st_nlink,my_uinfo->pw_name,my_ginfo->gr_name,sbuf.st_size,daybuf[clock->tm_mon],clock->tm_mday,clock->tm_hour,clock->tm_min);
}
运行结果