在Linux上实现自定义的 ls命令
文章目录
- 三、编写ls命令
- 1. 阅读联机帮助
- 2. ls是如何工作的
- 3. 如何编写ls
- 4. 改进ls命令
- 5. ls -l 是如何工作的
- 6. 如何编写 ls -l
三、编写ls命令
1. 阅读联机帮助
列出有关文件的信息(默认为当前目录)。如果未指定 -cftuvSUX 或 --sort,则按字母顺序对条目进行排序。
可以看到,ls命令 能够找出当前目录中所有文件的文件名,按字典序排序后输出。
ls命令 还能显示其他信息,如果加上 -l 选项,ls 会列出每个文件的详细信息,也叫 ls的长格式,在 man手册 中可以看到:
使用长列表格式
现在在我们的终端键入命令:
通过实验和联机帮助可以知道 ls 做了以下两件事(ls 能判定参数指定的是文件还是目录):
- 列出目录的内容
- 显示文件的信息
在正式开始之前,来看一下 Unix 是如何组织磁盘上的文件的。
大方框表示目录,大方框内的小方框表示文件,目录之间的连线表示目录之间的组织关系。
2. ls是如何工作的
通过联机帮助(过程省略)可以知道,从目录读数据与从文件读数据是类似的, opendir 打开一个目录,readdir 返回目录中的当前项,closedir 关闭一个目录,seekdir、telldir、rewinddir与 lseek 的功能类似。
接下来用 man手册 查询一下 readdir(3) ,可以看到:
readdir() 函数返回一个指向 dirent 结构的指针,该结构表示 dirp 指向的目录流中的下一个目录条目。它在到达目录流末尾或发生错误时返回 NULL。
也就是说, readdir() 来读取 struct dirent
获得目录中的记录。
3. 如何编写ls
最初级的ls命令
下面实现了一个最初级的 ls命令
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h> // opendir() readdir() closedir()void do_ls(char*);int main(int argc, char* argv[]) {if (argc == 1) {do_ls(".");}else {while (--argc) {printf("%s:\n", *(++argv));do_ls(*argv);}}return 0;
}void do_ls(char dirname[]) {DIR* dir_ptr; // 记录opendir()后的返回值struct dirent* direntp; // 记录readdir()后的返回值if ((dir_ptr = opendir(dirname)) == NULL) {fprintf(stderr, "ls1: cannot open %s\n", dirname);}else {while ((direntp = readdir(dir_ptr)) != NULL) {printf("%s\n", direntp->d_name);}closedir(dir_ptr);}
}
运行结果:
4. 改进ls命令
加入以下功能:
排序
解决办法:把所有的文件名读入一个数组,用qsort函数排序分栏:标准的 ls 输出是分栏排列的,有些以行排列,有些以列排列
解决办法:把文件名读入数组,然后计算出列的宽度和行数“.”
文件:ls 列出了“.”
文件,而标准的 ls只有在给出 -a 选项时才会列出
解决办法:使 ls1 能够接收选项 -a,并在没有 -a 的时候不显示隐藏文件选项 -l:如果选项中有 -l,标准的 ls会列出文件的详细信息,而 ls1不会
解决办法: 下面讨论
5. ls -l 是如何工作的
下面我们将把 ls -l 拆分成几个小组件逐一分析并实现:
1. 用 stat 得到文件信息
根据 man手册 所提供的信息,再去查询 fstatat,可以看到如下的信息:
下面写一个程序将以上我们需要的属性显示出来:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>void show_stat_info(char* fname, struct stat* buf);int main(int argc, char* argv[]) {struct stat info;if (argc > 1) {if (stat(argv[1], &info) != -1) {show_stat_info(argv[1], &info);return 0;}else {perror(argv[1]);} }return 0;
}void show_stat_info(char* fname, struct stat* buf) {printf(" mode: %o\n", buf->st_mode);printf(" links: %d\n", buf->st_nlink);printf(" user: %d\n", buf->st_uid);printf(" group: %d\n", buf->st_gid);printf(" size: %d\n", buf->st_size);printf("modtime: %d\n", buf->st_mtim.tv_sec);printf(" name: %s\n", fname);
}
运行结果:
对比可以看到,链接数、文件大小 的显示都没问题,最后修改时间是 time_t 类型,用 ctime 将其转换为字符串也可以解决。
为了进一步完善 ls -l ,我们需要进一步处理 模式、用户名和组 的显示
2. 将模式字段转换成字符
st_mode 是一个16位的二进制数,文件类型和权限被编码在这个数中,如图所示:
- 其中前 4 位用作文件类型,最多可以标识 16 种类型,1 代表具有某个属性,0 代表没有,目前已经使用了其中的 7 个
- 接下来的 3 位是文件的特殊属性,1 代表具有某个属性,0 表示没有,这 3 位分别是 set-user-ID位、set-group-ID位 和 sticky位
- 最后的 9 位是许可权限,分为 3 组,对应 3 种用户,它们是文件所有者、同组用户和其他用户。
每组 3 位,分别是读、写和执行的权限(相应的地方如果是 1,就说明该用户拥有对应的权限,0 代表没有)
如何读取被编码的值?
利用 子域编码 与 掩码 的技术。
对 2 进制进行位与操作,即我们所说的解码。判断目录代码:
if((info.st_mode & 0170000) == 0040000) {printf("this is a directory");
}
通过掩码把其他无关的部分置为 0,再与表示目录的代码比较,从而判断这是否是一个目录。
更简单的方法是用 #include <sys/stat.h>
中的宏代替上述代码:
#define S_ISFIFO(m) (((m)&(0170000)) == (0010000))
#define S_ISDIR(m) (((m)&(0170000)) == (0040000))
#define S_ISCHR(m) (((m)&(0170000)) == (0020000))
#define S_ISBLK(m) (((m)&(0170000)) == (0060000))
#define S_ISREG(m) (((m)&(0170000)) == (0100000))
使用宏后就这样写代码:
if(S_ISDIR(info.st_mode)) {printf("this is a directory");
}
下面实现将模式字段转换为字符
#include <sys/stat.h>void mode_to_letters(int mode, char str[]) {strcpy(str,"----------");if(S_ISDIR(mode)) str[0]='d';if(S_ISCHR(mode)) str[0]='c';if(S_ISBLK(mode)) str[0]='b';if(mode & S_IRUSR) str[1]='r';if(mode & S_IWUSR) str[2]='w';if(mode & S_IXUSR) str[3]='x';if(mode & S_IRGRP) str[4]='r';if(mode & S_IWGRP) str[5]='w';if(mode & S_IXGRP) str[6]='x';if(mode & S_IROTH) str[7]='r';if(mode & S_IWOTH) str[8]='w';if(mode & S_IXOTH) str[9]='X';
}
现在还剩下最后一个要解决的问题,文件所有者(user) 和 组(group) 的表示
3. 将用户/组 ID转换成字符串
用户
这里需要用到库函数 getpwuid() 来访问用户列表,getpwuid 需要 UID(user ID)作为参数,返回一个指向 struct passwd 的指针,这个结构定义在 /usr/include/pwd.h
中,通过 man手册查询 getpwuid 可以看到:
继续往下翻,
结构体 struct passwd 正是 ls -l 所需要的信息,实现代码:
#include <pwd.h>char* uid_to_name(uid_t uid) {return getpwuid(uid)->pw_name;
}
这段代码很简单,但不够健壮,如果 uid 不是一个合法的用户 ID,那 getpwuid 返回空指针 NULL,这时 getpwuid(uid)->pw_name 失去了意义。
常用的 ls命令 有一种处理这种情况的办法。(这里不做讨论)
组
文件 /etc/group
是一个保存所有的组信息的文本文件。在网络计算系统中,组信息也被保存在 NIS 中。(另外,前面讨论的 用户的信息 保存在 NIS 中)
Unix 系统提供 getgrgid()
函数来访问组列表,通过 man手册查询 getgrgid 可以看到:
继续往下翻,
实现代码:
#include <grp.h>char* gid_to_name(gid_t gid) {return getgrgid(gid)->gr_name;
}
6. 如何编写 ls -l
通过上面的分析,下面实现最终的代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h> // mode_to_letters() show_stat_info()
#include <dirent.h> // opendir() readdir() closedir()
#include <pwd.h> // getpwuid()
#include <grp.h> // getgrgid()
#include <string.h> // strcpy()
#include <time.h> // ctime()void do_ls(char dirname[]);
void dostat(char* filename);
void show_file_info(char* fname, struct stat* buf);
// void mode_to_letters(int mode, char str[]);
char* uid_to_name(uid_t uid);
char* gid_to_name(gid_t gid);int main(int argc, char* argv[]) {if (argc == 1) {do_ls(".");}else {while (--argc) {printf("%s:\n", *(++argv));do_ls(*argv);}}return 0;
}void do_ls(char dirname[]) {DIR* dir_ptr; // 记录opendir()后的返回值struct dirent* direntp; // 记录readdir()后的返回值if ((dir_ptr = opendir(dirname)) == NULL) {fprintf(stderr, "ls1: cannot open %s\n", dirname);}else {while ((direntp = readdir(dir_ptr)) != NULL) {// printf("%s\n", direntp->d_name);dostat(direntp->d_name);}closedir(dir_ptr);}
}void dostat(char* filename) {struct stat info; // 存储filename的信息if (stat(filename, &info) == -1) {perror(filename);}else {show_file_info(filename, &info);}
}void show_file_info(char* filename, struct stat* info_p) {// char* uid_t_name(), *ctime(), *gid_to_name();void mode_to_letters();char modestr[11];mode_to_letters(info_p->st_mode, modestr); // 将模式字段转换成字符printf("%s", modestr);printf("%4d", (int)info_p->st_nlink);printf(" %-10s", uid_to_name(info_p->st_uid)); // 将用户ID转换成字符串printf("%-10s", gid_to_name(info_p->st_gid)); // 将组ID转换成字符串printf("%8ld", (long)info_p->st_size);printf("%.12s", 4 + ctime(&info_p->st_mtim.tv_sec)); // 通过ctime()函数转换时间,之前的who命令有用到printf(" %s\n", filename);
}// 将模式字段转换成字符
void mode_to_letters(int mode, char str[]) {strcpy(str,"----------");// 用到了 子域编码 与 掩码 的技术if(S_ISDIR(mode)) str[0]='d';if(S_ISCHR(mode)) str[0]='c';if(S_ISBLK(mode)) str[0]='b';if(mode & S_IRUSR) str[1]='r';if(mode & S_IWUSR) str[2]='w';if(mode & S_IXUSR) str[3]='x';if(mode & S_IRGRP) str[4]='r';if(mode & S_IWGRP) str[5]='w';if(mode & S_IXGRP) str[6]='x';if(mode & S_IROTH) str[7]='r';if(mode & S_IWOTH) str[8]='w';if(mode & S_IXOTH) str[9]='X';
}// 将用户ID转换成字符串
char* uid_to_name(uid_t uid) {// struct passwd* getpwuid(), *pw_ptr;struct passwd* pw_ptr;static char numstr[10];if ((pw_ptr = getpwuid(uid)) == NULL) {sprintf(numstr, "%d", uid);return numstr;}else {return pw_ptr->pw_name;}
}// 将组ID转换成字符串
char* gid_to_name(gid_t gid) {// struct group* getgrgid(), *grp_str;struct group* grp_str;static char numstr[10];if ((grp_str = getgrgid(gid)) == NULL) {sprintf(numstr, "%d", gid);return numstr;}else {return grp_str->gr_name;}
}
运行结果:
我们自己编写的 ls2 对比 系统提供的 ls -l 效果已经很不错了,模式字段、用户名和组名的处理均已完成。剩下的 隐藏 “.”
,显示记录总数等功能暂时不再讨论。
在Linux上实现自定义的 ls命令相关推荐
- linux没有jre文件夹,linux上配置jdk时,java命令提示没有此文件或文件夹的解决方法...
linux上配置jdk时,java命令提示没有此文件或文件夹的解决方法 出现这个问题可能有以下几种原因: 1.对该文件没有执行的权限. 2.我们的机器是64位的,而下载的jdk是32位的. 我就是后一 ...
- Java:Linux上java -jar xxx.jar命令执行jar包时出现Error: Invalid or corrupt jarfile xxx.jar解决方案
Java:Linux上java -jar xxx.jar命令执行jar包时出现Error: Invalid or corrupt jarfile xxx.jar解决方案 参考文章: (1)Java:L ...
- linux sftp怎样支持通配符,linux上的sftp与scp命令
linux下的sftp与scp命令 第一个(sftp安全文件传输)是一个类ftp的客户端程序,它能够被用来在网络中传输文件.它并不使用FTP守护进程(ftpd或wu-ftpd)来进行连接,而是有意义地 ...
- Linux文件与目录操作 ls 命令(2)
说文件操作是最频繁地操作也不为过,在Linux中,使用ls命令可以列出当前目录中所有内容,本篇就先说说ls命令.本文所说的文件指文件和目录. ls命令常用选项 -a:显示指定目录下所有子目录与文件,包 ...
- linux服务器 图片压缩,Pngquant:Linux上压缩PNG图像的命令行实用工具
[51CTO.com快译]Pngquant是一款免费开源的跨平台命令行有损PNG压缩工具.它基于可移植的libimagequant库,用C99编写.可通过将PNG图像转换成更高效的8位PNG格式来显著 ...
- linux写一个ls命令,linux 下 如何自己写 ls 命令
有过linux 基础 都知道 ls 命令的作用 下面给出实现代码 #include #include #include #include #include #include #include #inc ...
- Linux使用C语言实现ls命令
原理 在linux下使用C语言,通过调用Linux系统的目录访问API来实现一个类似于ls命令功能的小程序,主要是可以练习程序对命令的解析和目录API函数的使用. 实现代码 #include < ...
- linux中ls-f的用法,ls命令--Linux命令应用大词典729个命令解读
内容来源于人民邮电出版社<Linux命令应用大词典> 讲述729个命令,1935个例子 学习Linux系统的参考书.案头书,遇到不懂的命令或命令选项一查即可 争取每天都发布内容 本文出自 ...
- 使用ls命令查看Linux的目录结构,linux查看工作目录文件ls命令用法详解
查看工作目录文件ls ls命令是Linux下最常用的命令.ls命令就是list的缩写.默认情况下ls用来查看当前目录的清单,如果ls指定其他目录,那么就会显示指定目录里的文件及文件夹清单.通过ls命令 ...
最新文章
- 极端值目标值(exterem or outlier target)对应的核心特征的分布差异分析+结合catboost特种重要度(top10)
- Spring Cloud Stream 学习小清单
- ibatis提示Unable to load embedded resource from assembly Entity.Ce_SQL.xml,Entity.
- oracle ogg常用指令,oracle goldengate日常管理命令
- asp.net core监控—引入Prometheus(一)
- JavaWeb项目实战(1)数据库环境搭载
- java 堆排序算法_堆排序算法的讲解及Java版实现
- mysql ssh 导入时注意问题
- 北京二环内详细矢量地图(MapInfo格式)
- 【Java后台】从零开始的Java后台开发(一)
- GIS相关网站、社区、论坛收藏
- win10浏览器闪退_Win10专业版下Edge浏览器闪退的多种解决技巧
- 2022为什么一定要学网络安全?
- 双河市谷歌高清卫星地图下载
- 您不知道Android的ES File Explorer可以做的19件事
- 5G标准核心内容:R15+R16(内含赠书福利)
- python编写古诗_用Python实现古诗词填字游戏(一)
- 网络协议 -- ICMP协议(1) 报文格式
- totolinkn200up怎么设置_TOTOLINK EX300无线中继器设置说明
- IBM V7000存储Mdisk磁盘掉线数据恢复_服务器数据恢复
热门文章
- [创业-17]:财务报表之综述
- 浅谈应变式扭矩传感器原理
- 56个民族select下拉框
- 计算机审计未来,关于计算机审计的几点看法
- GIT 技巧命令讲解笔记2020 (二)
- HTML+CSS完成的动画页面
- 微信:微信扫码支付、调用统一下单接口、网站支付 + springmvc
- 计算机体系结构 标量处理机
- fatal error: sdf sdf.hh: No such file or directory include sdf sdf.hh 解决办法
- 《爱因斯坦死亡方程式》——一部关于爱因斯坦传奇一生的纪录片