Linux下的系统编程指的是程序员使用系统调用或C语言本身所携带的库函数来设计和编写某一特定的程序
ls是日常所用的比较常见的命令之一,那么如何实现ls命令
首先实现ls要明确参数的作用是什么
ls -a 可以将目录中的所有文件(包括以.开头的文件)显示出来
ls -l 列出文件中的所有信息,包括文件的属性和权限等数据
ls -R 使用递归连同目录中的子目录中的文件显示出来,如果要显示隐藏文件就要添加-a参数
在实现ls之前我们需要了解如下的信息
用户权限所对应的表
字符长量值 | 字符常量对应的八进制值 | 含义 |
---|---|---|
S_IRUSR(S_IREAD) | 00400 | 文件所有者具有可读权限 |
S_IWUSR(S_IWRITE) | 00200 | 文件所有者具有可写入权限 |
S_IXUSR(S_IEXEC) | 00100 | 文件所有者具有可执行权限 |
S_IRGRP | 00040 | 用户组具有可读取权限 |
S_IWGRP | 00020 | 用户组具有可写入权限 |
S_IXGRP | 00010 | 用户组具有可执行权限 |
S_IROTH | 00004 | 其他用户具有可读权限 |
S_IWOTH | 00002 | 其他用户具有可写入权限 |
S_ISUID | 04000 | 文件的 (set user-id)位 |
S_ISGID | 02000 | 文件的(set group-id)位 |
S_ISVTX | 01000 | 文件的 sticky位 |
在ls的输出过程中,需要粗略的计算终端的宽度,在这里我介绍的是isatty和ioctl函数
isatty函数是用来判断目前所输出的设备是不是为终端设备
ioctl可以获得终端的设备信息,一些特殊的设备文件都可以利用这个来获得
具体用法如下:(在这个用法中,我将 terminalWidth 作为全局变量
void getWidth(){
struct winsize wbuf;
terminalWidth = 80;
if( isatty(STDOUT_FILENO) ){
if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &wbuf) == -1 || wbuf.ws_col == 0){
return ;
}
else
terminalWidth = wbuf.ws_col;
}
}
我们还应该了解如何获得文件的属性
#include < sys/types.h>
#include < sys/stat.h>
#include < unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char pathname, struct stat *statbuf);*
很多小伙伴们肯定对应该使用哪个函数感到迷惑,这三个函数的区别为, stat 用于获取由参数file name 所指定的文件名信息,保存在struct stat * buf 中,fstat 与 stat 的区别 fstat 是通过文件描述符来制定文件的, lstat 与 stat 的区别在于,对于 符号链接文件 , lstat 返回的是 符号链接本身, 而stat 指向的是文件状态信息
2。所以我在这里推荐大家是用的是,lstat 这个函数, 而对于如何读取链接文件,在这里我们可以使用 readlink 这个函数 来获取 文件中的链接
我在这里还遇到了一些错误,首先来跟大家说说readlink 这个函数应该如何应用
这个函数既是shell中的命令,又是操作系统提供的接口
通过查阅man 手册我们可以看到
#include < unistd.h>
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
#include < fcntl.h> /* Definition of AT_* constants */
#include < unistd.h>
ssize_t readlinkat(int dirfd, const char *pathname,
char *buf, size_t bufsiz);
首先它的返回值是ssize_t类型 ,在32位的机器上 它与 int 类型是一致的,而在 64 位的机器上 它的类型与 long int 类型是一致的
它可以读取目录中的内容但是它不以文件控制符号来结尾’\0’,也就是说必须得添加’\0’才能使用
我在实现readlink的过程中遇到了如下的错误:
1.没有添加’\0’导致每次输出自己所读取的链接就会遇到段错误,即访问了不该访问的内存
2.我采用的是动态数组,动态数组是在堆中申请的,而内核为了方便效率,在申请的动态数组中是有内容的,所以在使用的过程中动态数组必须进行初始化,不然就会读取其他很多不相干的内容
在 struct stat * buf 中是一个保存文件信息的结构体
struct stat {
dev_t st_dev;//文件的设备编号
ino_t st_ino;//文件的i-node编号
mode_t st_mode_t//文件的类型和存取的权限
nlink_t st_nink;//文件的硬链接数目
uid_t st_uid;//文件所有者的id
gid_t st_gid//文件所有组的id
dev_t st_rdev;//若文件为设备文件,则为其设备编号
off_t st_size//文件的大小
blksize_t st_blksize//文件系统的缓冲区
blkcnt_t st_blocks//占有文件的区块的个数
time_t st_atime//文件最近一次被访问的时间
time_t st_mtime//文件最后一次被修改的时间
time_t st_ctime//文件最近一次被更改的时间
st_mode所包含的文件信息
S_ISLNK 符号链接
S_ISREG 一般文件
S__ISDIR 目录文件
S_ISCHR 为字符设备文件
S_ISBLK 块设备文件
S_ISFIFO 判断是不是为FIFO
S_ISSOCK 判断是不是为 套接字
了解如何获取目录信息,只要对目录是由读的权限,就可以获取目录信息
1.opendir
DIR opendir(const char name)
用来获取参数指定的目录,并且返回DIR*的目录流,类似与文件操作中的文件描述符,接下来对目录的读取和搜索都要使用此返回值,成功则返回DIR * 的目录流
2.readdir
用来从DIR中读取中目录项信息,类似于链表的遍历输入完成后输出NULLd_name
3.closedir
关闭dir所指向的目录
用法如下
#include< sys/types.h>
#include< dirent.h>
#include < stdio.h>
int readir(const char * path)
{
DIR * dir;
struct dirent * prt;
if((dir = opendir(path)==NULL)
perror(“opendir”);
while((ptr=readdir(dir)!=NULL)
{
printf(“%d” ptr->d_name);
}
closedir(dir);
return 0;
}
在实现递归的过程中,可以采用 rewinddir 函数 十分重要
在这里我将解释以下 rewinndir的函数作用,以及为什么它在递归的过程中相当重要
头文件:
#include < sys/types.h>
#include < dirent.h>
定义的函数:
void rewinddir(DIR *dir);
官方给出的说明是rewinddir()用来设置参数dir 目录流目前的读取位置为原来开头的读取位置.
可以这么理解,打印出上级目录的时候已经进入到当前目录的最后一个,使用rewinddir就可以返回第一个文件中
我在实现 ls -R 遇到的问题即自己的解决办法
1.遭遇段错误
可能原因:栈溢出
-R 递归会产生巨大的文件量,使用普通的静态数组是无法满足这种需求,即使范围在大也不可以,在这里我们应该使用 动态数组或者 链表 来实现这个功能
2.在遍历/proc/目录的过程中偶尔会遭遇错误,大部分情况可以实现
可能原因:文件读取失败
解决方式; lstat 来获取文件信息如果获取失败就返回上层
/proc/是虚拟文件系统,它在里面保存进程,并且会定期清除,可能在读取的同时,和删除这个文件,即读取失败
在实现ls -R 的时候一定要明确自己思路
我的思路是,先输出第一层目录,然后通过rewinddir 返回到文件流,然后判断它是不是目录,如果是目录则进入,当一层没有目录的时候,就return 返回上层
代码奉上
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <dirent.h>
#include<stdlib.h>
#include<sys/ioctl.h>
int terminalWidth ;
char filenames[256][PATH_MAX];
char filenames1[256][100];
int alfag=0,lflag=0;//参数种类,对-l和-a,-r,-R进行判断
int cnt=0;
void Quicksort(int cnt1,char filenames2[][PATH_MAX],int start);
void recursion(char * path,int alfag);
int ls_prepare(char *path,int lflag);
int ls_prepare(char *pathname,int lflag);
int _ls(char *path,int lflag,int alfag);
int colormode,foreground,background;
void getcolor( char *filename ){