文章目录
- 引言
我们每个人在学习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中,多个文件可能有一点麻烦
- 全局变量的定义
在.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</