模拟实现ls的总结与感悟

  • 引言
    我们每个人在学习Linux的时候使用的第一个命令都应该是ls,这个命令也开启了我们学习Linux的大门,但是应该很少有人知道ls是如何实现的,所以我便在自己的能力范围内对ls进行模拟实现

前期准备1

argc与argv

我们在学习c语言的时候应该就应该对此有所了解了,其实main函数也是可以带参数的

argc,就是命令行所带的参数的个数,默认情况下argc是1
argv,就是命令行后面的字符串,默认情况下argv[0]=‘.’

ls . ..

ls后面带有两个参数,所以在1的基础上加上2
argv[0]=‘.’,argv[1]=“.”,argv[2]=“…”

ls 

ls后面什么参数都没有,那么argc就是1
argv[0]=“.”

getopt

是一个用来解析命令参数的函数,就不用我们自己实现命令参数的解析

#include<unist.h>
 int getopt(int argc, char * const argv[], const char *optstring);

其中argc就是参数的个数,
argv里面就是命令行的所有参数,
而optstring就是我们要解析的参数如:“ali”,我们要解析的参数就是-a,-l,-i

失败的话就返回-1

unistd里有个 optind 变量,每次getopt后,这个索引指向argv里当前分析的字符串的下一个索引2,因此argv[optind]就能得到下个字符串,通过判断是否以 '-'开头就可

stat与lstat

stat函数就是用来获取文件的信息
成功返回0,失败返回-1

include <sys/types.h>
 #include <sys/stat.h> 
 #include <unistd.h>
 int stat(const char *path, struct stat *buf)

path就是文件存储的路径,可以是相对路径也可以是绝对路径

struct 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 */ 最后一次改变该文件状态的时间   
};

struct stat中st_mode还有很多情况
我列举几个常见的

    S_IFMT   0170000    文件类型的位遮罩
    S_IFSOCK 0140000    套接字
    S_IFLNK 0120000     符号连接
    S_IFREG 0100000     一般文件
    S_IFBLK 0060000     区块装置
    S_IFDIR 0040000     目录
    S_IFCHR 0020000     字符装置
    S_IFIFO 0010000     先进先出
​
    S_ISUID 04000     文件的(set user-id on execution)位
    S_ISGID 02000     文件的(set group-id on execution)位
    S_ISVTX 01000     文件的sticky位
​
    S_IRUSR(S_IREAD) 00400     文件所有者具可读取权限
    S_IWUSR(S_IWRITE)00200     文件所有者具可写入权限
    S_IXUSR(S_IEXEC) 00100     文件所有者具可执行权限
​
    使用st_mode和S_IRGRP此类进行按位与&,如果为真就可以

    S_IRGRP 00040             用户组具可读取权限
    S_IWGRP 00020             用户组具可写入权限
    S_IXGRP 00010             用户组具可执行权限
​
    S_IROTH 00004             其他用户具可读取权限
    S_IWOTH 00002             其他用户具可写入权限
    S_IXOTH 00001             其他用户具可执行权限
​
    上述的文件类型在POSIX中定义了检查这些类型的宏定义:
    
    S_ISLNK (st_mode)    判断是否为符号连接
    S_ISREG (st_mode)    是否为一般文件
    S_ISDIR (st_mode)    是否为目录
    S_ISCHR (st_mode)    是否为字符装置文件
    S_ISBLK (s3e)        是否为先进先出
    S_ISSOCK (st_mode)   是否为socket
    若一目录具有sticky位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名,在linux中,最典型的就是这个/tmp目录啦。
​

我们可以使用st_mode和 S_IFMT进行&,得到的来判断其的文件类型
同时还可以使用S_ISDIR(st_mode)此类函数判断其文件类型,

与lstat的区别

对于普通文件的话,stat和lstat没有什么区别
而对于链接文件的话,调用lstat函数获取的是链接文件本身的属性信息;而stat函数获取的是链接文件指向的文件的属性信息
stat会找到其连接对应的目录,而lstat只会得到这个连接,不找其对应的目录

sprintf与fprintf

sprintf可以将格式化的数据存放到一个字符串里面

char fname[20];
sprintf(fname,"%s \%s","hello","world");
fname=“hello \world”;

fprintf是将格式化的数据写入到一个文件流中
与printf的区别是printf是将内容写到stdout的标准输出流里面(即打印到显示屏上)

opendir && closedir

opendir就是打开一个目录,可以类比打开一个文件

#include<sys/types.h>
#include<dirent.h>
  DIR* opendir (const char * path ); 

path就是存放文件的路径,
而DIR 相当于FILE的指针
失败就返回一个NULL

DIR 结构体的原型为:struct_dirstream

 struct __dirstream
   {
   
     void *__fd; /* `struct hurd_fd' pointer for descriptor.   */
     char *__data; /* Directory block.   */
     int __entry_data; /* Entry number `__data' corresponds to.   */
     char *__ptr; /* Current pointer into the block.   */
     int __entry_ptr; /* Entry number `__ptr' corresponds to.   */
     size_t __allocation; /* Space allocated for the block.   */
     size_t __size; /* Total valid data in the block.   */
     __libc_lock_define (, __lock) /* Mutex lock for this structure.   */
    };

closedir就是在每次调用完opendir后把目录关闭,避免内存泄漏

readdir

读取opendir目录返回值的列表,

#include<dirent.h>
struct dirent* readdir(DIR* dir_handle); 

返回dirent结构体的指针

struct dirent

{
   undefined

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字符 */

}

实现过程中遇到的麻烦

颜色控制

printf(“\033[31m hello \033[0m”)
划线处就是控制颜色输出的格式,黑体字就是我们需要他是什么颜色

背景色 字体色

40: 黑 30: 黑

41: 红 31: 红

42: 绿 32: 绿

43: 黄 33: 黄

44: 蓝 34: 蓝

45: 紫 35: 紫

46: 深绿 36: 深绿

47: 白色 37: 白色
记得在打印完之后,把颜色恢复成NONE[^ 4],不然再后面的打印都会跟着变色。

其他的printf格式就由你们去探索吧

Linux中多文件操作

我们在早期写写代码的时候,可以就在一个文件上写就可以了,但是如果一旦代码量大的话,一个文件就显得太少了,且每次查找也十分麻烦,那么我们就应该在多个文件中,进行编写代码

但是在Linux中,多个文件可能有一点麻烦


  1. 全局变量的定义
    在.h文件中
    最顶部加上这两行,ifndef后面的是任意都可以
    不过下面define后要添加一样的内容,在最后加上#endif
    之后声名全局变量,前加一个extern,不需要赋初值,在我们需要的.c文件中赋初值即可
#ifndef _LS_H_
#define _LS_H_
extern int a;
……

#endif

在.c文件中赋初值

int a=0;//前面也要加变脸类型

代码实现

我们会实现ls 以及 -r -l -t -R -s -i -a

  • ls.h

#ifndef _LS_H_
#define _LS_H_
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include<string.h>
#include<stdlib.h>

extern int aflag;
extern int lflag;//作为标识符,如果aflag lflag为1则有-a和-l这个参数,执行选项
extern int iflag;
extern int sflag;
extern int rflag;
extern int tflag;
extern int Rflag;
extern char filename[256][260];
extern long filetime[256</
  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevin~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值