Liunx 模拟实现 ls -l

模拟实现 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);
返回从 197011000秒到现在的时间---秒数

这样的秒数我们可以手动去转换,库中提供了 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);
}

运行结果
这里写图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现ls -l命令的关键是要读取当前目录下的文件和文件夹的信息,并将其格式化输出。下面是一个简单的C语言程序,实现了类似于ls -l的功能: ```c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <dirent.h> #include <sys/stat.h> #include <unistd.h> #include <pwd.h> #include <grp.h> #include <time.h> int main(int argc, char *argv[]) { DIR *dp; struct dirent *entry; struct stat statbuf; struct passwd *pwd; struct group *grp; char date[20]; if ((dp = opendir(".")) == NULL) { fprintf(stderr, "Cannot open directory.\n"); exit(1); } while ((entry = readdir(dp)) != NULL) { if (entry->d_name[0] == '.') continue; if (lstat(entry->d_name, &statbuf) < 0) continue; printf((S_ISDIR(statbuf.st_mode)) ? "d" : "-"); printf((statbuf.st_mode & S_IRUSR) ? "r" : "-"); printf((statbuf.st_mode & S_IWUSR) ? "w" : "-"); printf((statbuf.st_mode & S_IXUSR) ? "x" : "-"); printf((statbuf.st_mode & S_IRGRP) ? "r" : "-"); printf((statbuf.st_mode & S_IWGRP) ? "w" : "-"); printf((statbuf.st_mode & S_IXGRP) ? "x" : "-"); printf((statbuf.st_mode & S_IROTH) ? "r" : "-"); printf((statbuf.st_mode & S_IWOTH) ? "w" : "-"); printf((statbuf.st_mode & S_IXOTH) ? "x" : "-"); printf(" %2lu", statbuf.st_nlink); if ((pwd = getpwuid(statbuf.st_uid)) != NULL) { printf(" %s", pwd->pw_name); } else { printf(" %d", statbuf.st_uid); } if ((grp = getgrgid(statbuf.st_gid)) != NULL) { printf(" %s", grp->gr_name); } else { printf(" %d", statbuf.st_gid); } strftime(date, 20, "%b %d %H:%M", localtime(&statbuf.st_mtime)); printf(" %s", date); printf(" %s\n", entry->d_name); } closedir(dp); return 0; } ``` 这个程序使用了许多系统调用和库函数,比如opendir、readdir、lstat、getpwuid、getgrgid等等。它首先打开当前目录("."),然后循环读取目录下的每个文件和文件夹的信息,使用lstat函数获取文件的详细信息,再利用各种库函数将这些信息格式化输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值