写一个小轮子:模拟实现ls-l 指令

2.2k 词

概述

在Linux系统中,ls -l命令是我们日常使用最频繁的命令之一,它能够以长格式显示文件的详细信息。本文将深入解析ls -l的实现原理,并逐步讲解如何用C语言实现一个简化版的ls -l命令。

// 输出示例:-rw-rw-r-- 1 miao miao 4 9月 15 20:28 a.txt
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>

int main(int argc, char* argv[]){
    if (argc < 2){
        printf("%s filename\n", argv[0]);
        return -1;
    }

    // 通过stat函数获取文件信息
    struct stat st;
    int ret = stat(argv[1], &st);

    if (ret == -1){
        perror("stat");
        return -1;
    }

    // 1. 文件类型
    char perms[12] = {0};  
    switch (st.st_mode & __S_IFMT){
        case __S_IFLNK:
            perms[0] = 'l'; // 符号链接
            break;
        case __S_IFDIR:
            perms[0] = 'd'; // 目录
            break;
        case __S_IFREG:
            perms[0] = '-'; // 普通文件
            break;
        case __S_IFBLK:
            perms[0] = 'b'; // 块设备
            break;
        case __S_IFCHR:
            perms[0] = 'c'; // 字符设备
            break;
        case __S_IFSOCK:
            perms[0] = 's'; // 套接字
            break;
        case __S_IFIFO:
            perms[0] = 'p'; // 管道
            break;
        default:
            perms[0] = '?'; // 未知类型
            break;
    }

    // 2. 文件权限
    // 文件所有者权限
    perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
    perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
    perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';
    // 文件所在组权限
    perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
    perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
    perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
    // 其他人权限
    perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
    perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
    perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';
    perms[10] = '\0'; // 字符串结束符

    // 3. 硬链接数
    int linknum = st.st_nlink;

    // 4. 文件所有者名称
    char* fileuser = getpwuid(st.st_uid)->pw_name;

    // 5. 文件所在组
    char* filegroup = getgrgid(st.st_gid)->gr_name;

    // 6. 文件大小
    long int filesize = st.st_size;

    // 7. 修改时间
    char* time = ctime(&st.st_mtime);
    char mtime[512] = {0};
    strncpy(mtime, time, strlen(time) - 1); // 去除末尾的换行符

    // 格式化输出
    char buf[1024];
    sprintf(buf, "%s %d %s %s %ld %s %s", 
            perms, linknum, fileuser, filegroup, filesize, mtime, argv[1]);
    printf("%s\n", buf);
    
    return 0;
}

1. stat系统调用

stat()函数是获取文件信息的核心系统调用,它填充一个stat结构体,包含文件的所有元数据:

  • 文件类型和权限(st_mode)
  • 硬链接数量(st_nlink)
  • 所有者和组ID(st_uid, st_gid)
  • 文件大小(st_size)
  • 最后修改时间(st_mtime)

2. 文件类型识别

通过st_mode & __S_IFMT这种掩码的方式,可以提取文件类型信息,常见的文件类型包括:

  • 普通文件(-)
  • 目录(d)
  • 符号链接(l)
  • 字符设备(c)
  • 块设备(b)
  • 套接字(s)
  • 管道(p)

3. 权限位处理

文件权限分为三组:所有者、组和其他用户,每组包含读(r)、写(w)和执行(x)权限。通过位掩码操作可以检查每位权限设置。

4. 用户和组信息转换

使用getpwuid()getgrgid()函数将UID和GID转换为可读的用户名和组名。

5. 时间格式处理

ctime()将时间戳转换为可读字符串,但包含换行符,需要去除末尾的换行符。

示例:

ll输出:

脚本输出: