热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux下C语言实现ls命令以及lsl命令

文章目录前期准备DIR结构体dirent结构体stat结构体st_mode结构体ls的实现大致思路以下是源代码:运行结果:ls-l的实现大致思路:1.total值的计算2.文件时间


文章目录

      • 前期准备
        • DIR结构体
        • dirent结构体
        • stat结构体
        • st_mode结构体
      • ls的实现
        • 大致思路
        • 以下是源代码:
        • 运行结果 :
      • ls -l 的实现
        • 大致思路:
          • 1. total值的计算
          • 2. 文件时间的显示
          • tm结构体
        • 以下是源代码:
        • 运行结果 :


前期准备


DIR结构体

首先开始先来看看DIR这个结构体 , 以下为DIR结构体的定义 :

struct __dirstream { void *__fd; char *__data; int __entry_data; char *__ptr; int __entry_ptr; size_t __allocation; size_t __size; __libc_lock_define (, __lock) }; typedef struct __dirstream DIR;

DIR结构体类似于FILE,是一个内部结构,以下几个函数用这个内部结构保存当前正在被读取的目录的有关信息(摘自《UNIX环境高级编程(第二版)》).
函数 DIR *opendir(const char *pathname),即打开文件目录,返回的就是指向DIR结构体的指针,而该指针由以下几个函数使用:

struct dirent *readdir(DIR *dp); void rewinddir(DIR *dp); int closedir(DIR *dp); long telldir(DIR *dp); void seekdir(DIR *dp,long loc);

关于DIR的结构 , 我们了解这么对就可以了.


dirent结构体

接着在来看看dirent结构体 , 首先我们要弄清楚目录文件(directory file)的概念:这种文件包含了其他文件的名字以及指向与这些文件有关的信息的指针(摘自《UNIX环境高级编程(第二版)》) .从定义能够看出,dirent不仅仅指向目录,还指向目录中的具体文件,readdir函数同样也读取目录下的文件,这就是证据。以下为dirent结构体的定义:

struct dirent { long d_ino; /* inode number 索引节点号 */ off_t d_off; /* offset to this dirent 在目录文件中的偏移 */ unsigned short d_reclen; /* length of this d_name 文件名长 */ unsigned char d_type; /* the type of d_name 文件类型 */ char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */ }

从上述定义也能够看出来,dirent结构体存储的关于文件的信息很少,所以dirent同样也是起着一个索引的作用,如果想获得类似ls -l那种效果的文件信息,必须要靠stat函数了 .


stat结构体

通过readdir函数读取到的文件名存储在结构体dirent的d_name成员中,而函数

int stat(const char *file_name, struct stat *buf);

的作用就是获取文件名为d_name的文件的详细信息,存储在stat结构体中。以下为stat结构体的定义:

struct stat { mode_t st_mode; //文件访问权限 ino_t st_ino; //索引节点号 dev_t st_dev; //文件使用的设备号 dev_t st_rdev; //设备文件的设备号 nlink_t st_nlink; //文件的硬连接数 uid_t st_uid; //所有者用户识别号 gid_t st_gid; //组识别号 off_t st_size; //以字节为单位的文件容量 time_t st_atime; //最后一次访问该文件的时间 time_t st_mtime; //最后一次修改该文件的时间 time_t st_ctime; //最后一次改变该文件状态的时间 blksize_t st_blksize; //包含该文件的磁盘块的大小 blkcnt_t st_blocks; //该文件所占的磁盘块 };

这样前期的准备工作就做的差不多了 , 让我们来看看ls怎么实现 .


st_mode结构体

st_mode这个变量用来判断文件类型
st_mode是用特征位来表示文件类型的,特征位的定义如下:

S_IFMT 0170000 文件类型的位遮罩
S_IFSOCK 0140000 socket
S_IFLNK 0120000 符号链接(symbolic link)
S_IFREG 0100000 一般文件
S_IFBLK 0060000 区块装置(block device)
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符装置(character device)
S_IFIFO 0010000 先进先出(fifo)
S_ISUID 0004000 文件的(set user-id on execution)
S_ISGID 0002000 文件的(set group-id on execution)
S_ISVTX 0001000 文件的sticky位
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 其他用户具可执行权限
摘自《Linux C 函数库参考手册》

ls的实现


大致思路

大致的思路是(因为时间过的有点长 , 当时是有些具体的想法都忘记了 , 将就着看吧) :
编者要完成的是一个经过排序的ls .
首先根据DIR这个结构体 , 打开目录 , 再根据dirent这个结构体 , 读出目录中的文件名 , 并把它保存在一个二维数组中(编者这里将数组设置成了100*100 , 足够保存) , 然后对于目录文件和可执行文件进行特殊标记 (这里就是在最后 , 标记一个数字 , 因为文件名肯定取不到) , 因为在Linux中不同的文件的颜色是不同的, 然后使用快排对文件名进行排序 , 然后在输出的时候对后最一个位置进行甄别 ,如果是可执行文件或者目录的话, 使其输出相应的颜色.
对于文件名的快排 , 附上链接同大家学习.
https://blog.csdn.net/f_zyj/article/details/51484751


以下是源代码:

#include
#include
#include
#include
#include
#include
#include
#define SIZE_MAX 100int cmp(const void *a,const void *b)
{return (strcmp((char*)a,(char*)b));
}int main()
{char path[256] &#61; {0};getcwd(path,256);DIR *pdir &#61; opendir(path);if(pdir &#61;&#61; NULL){exit(1);}struct dirent *p &#61; NULL;struct stat st;int count &#61; 0;char s[SIZE_MAX][SIZE_MAX];while((p &#61; readdir(pdir)) !&#61; NULL){if(strncmp(p->d_name,".",1) &#61;&#61; 0){continue;}stat(p->d_name,&st);if(S_ISDIR(st.st_mode)){s[count][SIZE_MAX -1] &#61; &#39;9&#39;;}else{if(st.st_mode&(S_IXUSR | S_IXGRP | S_IXOTH)){s[count][SIZE_MAX -1] &#61; &#39;8&#39;;}}strcpy(s[count&#43;&#43;],p->d_name);}qsort(s,count,sizeof(s[0]),cmp);int i &#61; 0;for(i &#61; 0;i < count;i&#43;&#43;){if(s[i][SIZE_MAX -1] &#61;&#61; &#39;9&#39;){printf("\033[1;34m%s \033[0m",s[i]);}else{if(s[i][SIZE_MAX -1] &#61;&#61; &#39;8&#39;){printf("\033[1;32m%s \033[0m",s[i]);}else{printf("%s ",s[i]);}}}printf("\n");closedir(pdir);exit(0);
}

运行结果 :

在这里插入图片描述


ls -l 的实现


大致思路:

获取当前的工作目录 , 讲读取到的文件都写入 自己创建的二维数组当中 , 然后进行快排 , 之后再与DIR结构结构体中读取到的目录进行比对 , 根据 st_mode 来对文件进行判断 , 然后在进行输出(跟ls的实现基本相同).
那么来说说在编写的时候遇到的问题 :


  • 1.total值的计算
  • 2.文件时间的显示

1. total值的计算

由于total的单位是k , 4096是块的默认大小 . 所以需要做的是 , 将文件所有的文件大小加起来 , 然后计算出所占的块数(不满一块的按照一块来计算) , 然后根据 total &#61; 块数 * 4 , 就能得出total的值.
注意 : ls -l 是不计算符号链接的 .


2. 文件时间的显示

在ls -l的命令中 , 会显示文件的创建时间 . 所以要使用 localtime() 用来获取系统时间(精度为秒)
, 这样的话就能正确输出时间.


tm结构体

#ifndef _TM_DEFINED
struct tm {int tm_sec; /* 秒 – 取值区间为[0,59] */int tm_min; /* 分 - 取值区间为[0,59] */int tm_hour; /* 时 - 取值区间为[0,23] */int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */int tm_mon; /* 月份&#xff08;从一月开始&#xff0c;0代表一月&#xff09; - 取值区间为[0,11] */int tm_year; /* 年份&#xff0c;其值等于实际年份减去1900 */int tm_wday; /* 星期 – 取值区间为[0,6]&#xff0c;其中0代表星期天&#xff0c;1代表星期一&#xff0c;以此类推 */int tm_yday; /* 从每年的1月1日开始的天数 – 取值区间为[0,365]&#xff0c;其中0代表1月1日&#xff0c;1代表1月2日&#xff0c;以此类推 */int tm_isdst; /* 夏令时标识符&#xff0c;实行夏令时的时候&#xff0c;tm_isdst为正。不实行夏令时的进候&#xff0c;tm_isdst为0&#xff1b;不了解情况时&#xff0c;tm_isdst()为负。*/};
#define _TM_DEFINED
#endif

以下是源代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZE_MAX 100int cmp(const void *a,const void *b)
{return (strcmp((char*)a,(char*)b));
}int main()
{char path[256] &#61; {0};getcwd(path,256);DIR *pdir &#61; opendir(path);if(pdir &#61;&#61; NULL){exit(1);}struct dirent *p &#61; NULL;struct stat st;int total &#61; 0;long block&#61;0;char s[SIZE_MAX][SIZE_MAX] &#61; {0};int count &#61; 0;while((p &#61; readdir(pdir)) !&#61; NULL)//计算total{stat(p->d_name,&st);if(strncmp(p->d_name,".",1) &#61;&#61; 0){continue;}if(st.st_size <&#61; 4096){block&#43;&#43;;}else{block &#43;&#61; (st.st_size/4096) &#43;1;}strcpy(s[count&#43;&#43;],p->d_name);}qsort(s,count,sizeof(s[0]),cmp);closedir(pdir);total &#61; block*4;printf("total %d\n",total);int i &#61; 0;for(i &#61; 0;i < count;i&#43;&#43;){pdir &#61; opendir(path);while((p &#61; readdir(pdir)) !&#61; NULL){if(strncmp(p->d_name,".",1) &#61;&#61; 0){continue;} if(strcmp(p->d_name,s[i]) !&#61; 0){continue;}stat(p->d_name,&st);if(S_ISDIR(st.st_mode))//判断是否为目录{printf("d");}if((st.st_mode & S_IFREG) &#61;&#61; S_IFREG)//是否为普通文件{printf("-");}if((st.st_mode & S_IFLNK) &#61;&#61; S_IFLNK)//是否为链接文件{printf("l");}if((st.st_mode & S_IFCHR) &#61;&#61; S_IFCHR)//是否为字符特殊文件{printf("c");}if((st.st_mode & S_IFBLK) &#61;&#61; S_IFBLK)//是否为块特殊文件{printf("b");}if((st.st_mode & S_IFIFO) &#61;&#61; S_IFIFO)//是否为管道或FIFO文件{printf("p");}if((st.st_mode & S_IFSOCK) &#61;&#61; S_IFSOCK)//是否为套接字文件{printf("s");}if((st.st_mode & S_IRUSR) &#61;&#61; S_IRUSR){printf("r"); }else{printf("-");}if((st.st_mode & S_IWUSR) &#61;&#61; S_IWUSR){ printf("w");}else{printf("-");}if((st.st_mode & S_IXUSR) &#61;&#61; S_IXUSR){printf("x");}else{printf("-");}if((st.st_mode & S_IRGRP) &#61;&#61; S_IRGRP){printf("r");}else{printf("-");}if((st.st_mode & S_IWGRP) &#61;&#61; S_IWGRP){printf("w");}else{printf("-");}if((st.st_mode & S_IXGRP) &#61;&#61; S_IXGRP){printf("x");}else{printf("-");}if((st.st_mode & S_IROTH) &#61;&#61; S_IROTH){printf("r");}else{printf("-");}if((st.st_mode & S_IWOTH) &#61;&#61; S_IWOTH){printf("w");}else{printf("-");}if((st.st_mode & S_IXOTH) &#61;&#61; S_IXOTH){printf("x");}else{printf("-");}printf(". ");printf("%d ",st.st_nlink);//硬连接数struct passwd *pwd;pwd &#61; getpwuid(st.st_uid);//所有者用户识别码printf("%-4s ",pwd->pw_name);struct group *grp;grp &#61; getgrgid(st.st_gid);//组识别码printf("%-4s ",grp->gr_name);printf("%5d ",st.st_size);//以字节为单位的文件容量struct tm *t &#61; localtime(&st.st_mtime);//转换为人类可识别的时间char *wday[12] &#61; {"Jan","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec"};printf("%s %d ",wday[t->tm_mon],t->tm_mday);if(t->tm_hour < 10){printf("0%d:",t->tm_hour);}else{printf("%d:",t->tm_hour);}if(t->tm_min < 10){printf("0%d ",t->tm_min);}else{printf("%d ",t->tm_min);}if(S_ISDIR(st.st_mode)){printf("\033[1;34m%s \n\033[0m",p->d_name);}else{if(st.st_mode&(S_IXUSR | S_IXGRP | S_IXOTH)){printf("\033[1;32m%s \n\033[0m",p->d_name);}else{printf("%s \n",p->d_name);}} }closedir(pdir);}exit(0);
}

运行结果 :

在这里插入图片描述

编者的这种方法仅仅是为了完成而完成 , 它的效率并不高 , 尤其是在判断文件种类的时候 .这代码仅供学习吧 , 如果有人有更好的方法 , 可以来交流.


推荐阅读
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • 【妙】bug称它为数组越界的妙用
    1、聊一聊首先跟大家推荐一首非常温柔的歌曲,跑步的常听。本文主要把自己对C语言中柔性数组、零数组等等的理解分享给大家,并聊聊如何构建一种统一化的学习思想 ... [详细]
  • iOS 不定参数 详解 ... [详细]
  • malloc 是 C 语言中的一个标准库函数,全称为 memory allocation,即动态内存分配。它用于在程序运行时申请一块指定大小的连续内存区域,并返回该区域的起始地址。当无法预先确定内存的具体位置时,可以通过 malloc 动态分配内存。 ... [详细]
  • MySQL初级篇——字符串、日期时间、流程控制函数的相关应用
    文章目录:1.字符串函数2.日期时间函数2.1获取日期时间2.2日期与时间戳的转换2.3获取年月日、时分秒、星期数、天数等函数2.4时间和秒钟的转换2. ... [详细]
  • T15483.【清华集训2017模拟11.26】简单路径T25484.【清华集训2017模拟11.26】快乐树T35485.【清华集训2017模拟11.26】字符串T1结论题,结论很 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • [c++基础]STL
    cppfig15_10.cppincludeincludeusingnamespacestd;templatevoidprintVector(constvector&integer ... [详细]
  • 本文介绍如何使用线段树解决洛谷 P1531 我讨厌它问题,重点在于单点更新和区间查询最大值。 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • 深入解析C语言中结构体的内存对齐机制及其优化方法
    为了提高CPU访问效率,C语言中的结构体成员在内存中遵循特定的对齐规则。本文详细解析了这些对齐机制,并探讨了如何通过合理的布局和编译器选项来优化结构体的内存使用,从而提升程序性能。 ... [详细]
  • 在分析Android的Audio系统时,我们对mpAudioPolicy->get_input进行了详细探讨,发现其背后涉及的机制相当复杂。本文将详细介绍这一过程及其背后的实现细节。 ... [详细]
  • 本文探讨了C语言和C++中大小写的处理方式,并详细介绍了如何在C++中实现不区分大小写的字符串比较。通过自定义`char_traits`类,可以灵活地处理字符的比较、复制和转换。 ... [详细]
  • 本文详细介绍了如何在Unity中实现一个简单的广告牌着色器,帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 题目《BZOJ2654: Tree》的时间限制为30秒,内存限制为512MB。该问题通过结合二分查找和Kruskal算法,提供了一种高效的优化解决方案。具体而言,利用二分查找缩小解的范围,再通过Kruskal算法构建最小生成树,从而在复杂度上实现了显著的优化。此方法不仅提高了算法的效率,还确保了在大规模数据集上的稳定性能。 ... [详细]
author-avatar
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有