获取文件属性:
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int stat(const char *filename,struct stat *buf);
int fstat(int filedes,struct stat *buf);
int lstat(const char *filename,struct stat *buf);
---
//区别:stat用于获取由参数filename指定的文件名的状态信息
fstat通过文件描述符指定文件
lstat返回的是符号链接文件本身的状态信息,而stat返回的是符号链接指向的文件状态信息
参数struct stat *buf是一个保存文件状态信息的结构体:
struct stat{
dev_t st_dev; //文件设备编号
ino_t st_ino; //文件节点编号
mode_t st_mode; //文件类型和存取权限
nlink_t st_nlink; //硬链接数目
uid_t st_uid; //文件所有者的用户id
gid_t st_gid; //组
dev_t st_rdev; //设别编号
off_t st_size; //文件大小
blksize_t st_blksize; //文件系统的I/O缓冲区大小
blkcnt_t st_blocks; //占用文件区块个数
time_t st_atime; //最近被访问
time_t st_mtime; //最后被修改
time_t st_ctime; //最近被更改
};
对于st_mode包含的文件类型信息:
S_ISLNK(st_mode) 符号链接
S_ISREG(st_mode) 一般文件
S_ISDIR(st_mode) 目录文件
S_ISCHR(st_mode) 字符设备文件
S_ISBLK(st_mode) 块设备文件
S_ISFIFO(st_mode) 先进先出FIFO
S_ISSOCK(st_mode) socket
设置文件属性:
hmod/chmod
chown/fchown/lchown
truncate/ftruncate //改变文件大小
utime
umask
文件的移动:
#include<stdio.h>
int rename(const char *oldpath,const char *newpath);
文件的删除:
#include<unistd.h>
int unlink(const char *pathname);
int remove(const char *pathname);
目录的创建和删除:
#include<sys/types.h>
#include<sys/stat.h>
int mkdir(const char *pathname,mode_t mode);
//空目录中会自动创建.和..目录项
//新创建目录的uid与创建该目录的进程的uid一致
int rmdir(const char *pathname) //只能删除空目录
获取当前目录:
当前工作目录通常是口令文件(/etc/passwd)中该用户登录项的第6个字段
#include<unistd.h>
char *getcwd(char *buf,size_t size);
char *get_current_dir_name(void);
char *getwd(char *buf);
设置工作目录:
#include<unistd.h>
int chdir(const char *path);
int fchdir(int fd);
获取目录信息:
#include<sys/types.h>
#include<dirent.h>
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);
编写ls的流程大概是这样的~
首先解析输入的参数,然后判断输入的参数中是否有目录或文件,如果有的话再打印参数中每个目录或文件的信息,没有的话打印当前目录下的文件信息。
鸭鸭写的ls实现了-a,-l-i,-r,-R功能。
总结以下几点:
①之前没有考虑到要加其他参数的时候,只是实现-a,-l-al,所以鸭鸭采用了很笨但是很直接的方法,用字符串比较函数判断-后面的字符串是否与-a,-l,-al相等,结果是可行的;但是在加了更多的参数之后,参数之间结合的种类不可能全部一一列举出来,这时候应该采用位运算方式,每个参数用整形值代表(0,1,2,4,8…),代表不同的位(存在为1,不存在为0),这样flagparam在进行或运算的时候只需要判断-后面是否有a,i,l,r,R就可以了,很方便。
②不难发现这5个参数之间是有相似之处的,比如r只是倒序输出,i只是要加上节点编号…都是在a,l的基础上做了一些小改动,鸭鸭可以先用与运算判断该参数是否存在,存在的话实现其功能,但是在输出的时候,用异或运算再将该位去掉,再以a或l或al的方式输出,这样在switch case的时候就减少了很多选项。
③使用全局变量既有好处也有坏处,好处就是函数之间不用麻烦的传参,可以直接在各函数内使用,坏处就是多次调用函数如果忘记对全局变量初始化会造成很严重的后果。所以鸭鸭有些使用了局部变量传参的方式,有些使用了全局变量,但是需要初始化。
④上个月记得鸭鸭问过康康学长一个问题,为什么总是要用这个错误处理函数,一方面是给用户提示信息,一方面是方便设计者查错。在程序未完成的时候,运行过程中查错很方便,遇到错误就exit,但是实际使用的时候,不能遇到错误就退出,所以鸭鸭在错误处理函数做了一些修改。
⑤在测试根目录的时候,有些目录没有访问的权限,换成sudo也是会有权限不足的情况,是因为存在一些目录root是没有访问权限的,所以在调用lstat函数获取文件信息失败时,要判断一下全局errno里存的值,当errno==13,代表此时访问的目录没有权限,这时候就忽略掉这个目录,程序继续往下跑。
⑥stat函数里的文件名参数一定是绝对路径下的文件名,否则获取文件属性失败(一把辛酸泪…)
⑦在实现对齐的时候,汉字不能像英文一样排好,是因为在Linux下一个汉字占3个字节,但是实际显示占2个字节,鸭鸭借鉴了gjldw的方法,写一个my_strlen函数,传入一个字符串,遇到汉字就sum++,再将结果除以3就是汉字的个数,那我们把字符串总字节数-汉字个数就得到了能正确显示的字节数。
⑧实现-R的功能有两种,一种是队列链表,一种是递归函数,大部分小伙伴都采用了第二种,包括鸭鸭,将文件名存在数组里,每递归一次就开一个数组,数组长度也要很大才能存的下文件名,所以在根目录下就炸了。。要避免堆栈溢出的方法就是使用malloc开动态数组,用完就free掉,可能鸭鸭写的还是有问题…根目录跑到/proc下就提示“已杀死”,这个问题目前还没有解决掉,可能代码还是要再改T.T
遇到的问题和坑点就这么多吧,最大的感受就是在写代码前一定要把思路理清,整体框架设计好,需要哪些函数,实现什么功能等等,如果做不到这几点的话,写的过程中可能会遇到很多问题,一个解决掉另一个又出来,导致你的代码很散乱,各种方法都行不通之后可能不得不重写,这样就费时费力,很不友好的哇。。
嗯~总结经验,获得进步!鸭鸭加油^_^