目标
- 实现 ls 的 -a、-l、-R、-t、-r、-i、-s 参数,并允许这些参数任意组合.
- -R 需要通对 / 的遍历测试
- 界面美观(输出对齐、与颜色显示等)
- 无资源与内存泄露
准备
书籍阅读
- <<Linux/Unix系统编程手册(上)>>
– 2, 3略读
– 4, 5.2-5.5, 15, 18 重点 - <<Unix_Linux编程实践教程>>
– 1, 2, 3
好文
- Linux C编程一站式学习(前言) (燃起了对linux系统编程学习的兴趣和信心)
实现
具体目标
- 命令行选项
- -a : 显示所有文件及目录 (包括. 开头的隐藏文件)
- -l : 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
- -R : 迭代显示目录下所有的子目录
- -t : sort by modification time, newest first
- -r : 将文件以相反次序显示(原定依英文字母次序)
- -i : 打印每个文件的index值
- -s : 显示以块为单位的文件大小
- 界面
- 对齐
- 颜色
- 高性能
- 无内存与资源泄露
BASE
- 文件函数
- ls的实现只需考虑文件和目录两种情况,无需考虑驱动器或分区(位于不同分区的目录通过文件树无缝连接到一起)
- 目录是一种特殊的文件,它的内容是 文件 和 目录的名字
- 文件名包含在目录中,而文件信息可用stat()函数得到
(上图中成员变量不含未被ls -l所使用的变量,并非stat.h的全部成员变量) - 字域掩码
ls的功能
- 列出目录的内容
- 显示文件的信息
基于上述两点,我们可以先编写一个最基本的ls程序,只要能list就行,后续再实现各种进阶目标
ls所需头文件:
<stdio.h>
<stdlib.h>(提供exit函数)
<sys/types.h>(基本系统数据类型)
<dirent.h>(用于目录操作)
ls的算法:
mian(){
opendir
while(readdir){
print d_name
}
closedir
}
SHOW ME THE CODE
#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#include<stdlib.h>
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);//print环节
}
}
}
void do_ls(char dirname[])
{
DIR *dir_ptr;//文件夹指针
struct dirent *direntp;//存放文件夹目录内容
if((dir_ptr = opendir(dirname)) == NULL){
//open环节
fprintf(stderr, "ls1: Cannot open %s\n",dirname);//打开失败
}else{
while((direntp = readdir(dir_ptr)) != NULL){
//while(readdir)
printf("%s\n",direntp -> d_name);//d_name:当前遍历子项的文件名,限制长度为256,包括字符串终止符’\0’
}
int flag;
if(( flag = closedir(dir_ptr)) == -1){
//close环节
perror("ls1: Cannot close dir");//关闭失败
exit(0);
}
}
效果示例:(基于mintOS)
-
查看当前目录
-
查看指定目录
ls -l 的实现
在以上的基础上,加入:
- stat函数
- 掩码判断文件类型(留个坑,有机会就写一篇掩码相关博客)
实现ls -l代码如下
#include<stdio.h>
#include<stdlib.h>
#include<dirent.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<pwd.h>
#include<grp.h>
void do_ls(char[]);//执行ls基本操作
void dostat(char *);//获取文件信息
void show_file_info(char *, struct stat *);//展示从stat获取的信息
void mode_to_letters(int ,char[]);//将模式字段转化为字符
char * uid_to_name(uid_t);//将用户ID转化为字符串
char * gid_to_name(gid_t);//将组ID转化为字符串
int main(int argc, char *argv[])
{
if(argc == 1){
do_ls(".");
}else{
while(--argc){
printf("%s:\n", * ++argv);
do_ls(*argv);
}
}
}
void do_ls(char dirname[])
//list files in directory called dirname
{
DIR *dir_ptr;
struct dirent *direntp;
if((dir_ptr = opendir(dirname)) == NULL){
perror("打开目录失败");
exit(0);
}else{
while((direntp = readdir(dir_ptr)) != NULL){
dostat(direntp -> d_name);//调用下一函数
}
int flag;
if((flag = closedir(dir_ptr)) == -1){
perror("关闭目录失败");
}
}
}
void dostat(char *filename)
{
struct stat info;//存放stat结构体数据的地址
if(stat(filename, &info) == -1){
//解析filename,将得到的信息放在info
perror("获取信息失败");
}else{
show_file_info(filename,&info);//调用下一函数
}
}
void show_file_info(char *filename, struct stat *info_p)//此处的info_p就是stat放东西的info位
//display the info about filename
{
char *uid_to_name(), *ctime(),