LinuxC编程——ls的实现(-R,-a,-l)

本文探讨Linux系统编程,以实现ls命令为例,解析ls的-R,-a,-l参数功能。讲解了如何获取文件属性,使用stat、fstat、lstat函数的区别,以及readlink函数的应用。此外,还介绍了如何处理递归目录,强调了rewinddir函数在递归过程中的关键作用,以及在实现ls -R时可能遇到的问题和解决方案。" 120292794,10309474,Java生态技术资源与下载官网汇总,"['Java开发', 'Spring框架', 'Maven构建', '微服务', 'Web前端', '容器技术']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 ){
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值