在该系列的上一篇中,介绍了文件概念和相关的诸多函数,但实例仅有两三个。本篇文章不再专门介绍函数,而是先承接上文,记录一下通过stat()
函数来实现ls -l命令。
1 ls -l命令的作用
ls -l
命令用于查看当前或指定目录下文件的详细信息,使用效果如下
1 | ls -l |
其中每行包含7个字段,分别
- 模式mode:第一个字符表示文件类型,后面表示文件访问权限
- 链接数links:即文件被引用的次数
- 文件所有者owner:用户名
- 组group:文件所有者所在的组
- 大小size:以字节bytes为单位
- 最后修改时间last-modified:较新文件会列出月日时分,较老文件只列出月日年
- 文件名name:最长为256个字节
2 ls -l 如何工作
上篇文章中,我们已经实现过不带选项的ls命令,两者的区别就在于一个只简单地输出文件名,而另一个则需要输出文件的详细信息。
那么文件信息如何获取呢?答案是使用之前已经介绍过的stat()
函数,关于该函数的介绍见上篇文章。
所以其原理就是:
- 打开当前或给定目录
- 循环读取目录中的文件,获取文件名
- 通过文件名来调用
stat()
函数来获取文件的详细信息 - 按照指定格式来输出文件信息。
第1步和第2步已经在之前ls的实现中有过练习,显然这里的主要工作就是通过stat()
获取文件信息和按照指定格式来输出信息。
3 获取文件信息
stat()
的返回值是一个struct stat
,这里需要分析一下如何从这个结构体中获取我们要在ls -l命令中打印的信息,结构体信息摘录于下。
1 |
|
3.1 获取mode信息
需要从成员st_mode
中获取,其类型mode_t
在我的系统中定义就是unsigned int
。但在命令ls -l的输出应该是类似-rw-rw-r--
的10个字符的字符串。这之间如何转换呢?
其实这些信息是以位图的方式放进了这个整数类型的16位中。
其中前4位用作文件类型,最多可标识16种类型,目前已有7种文件类型。
接下来3位是文件特殊属性,分别是set-user-ID,set-group-ID和sticky位,1表示具有该属性,0表示没有。
最后9位表示许可权限,分为3组,分别对应文件所有者、同组用户、其他用户的读写执行权限。同样有的话为1,没有的话为0。
既然是位图,那么必然可以通过掩码来取得其信息。如关于文件类型,在<sys/stat.h>
中定义了掩码S_IFMT
来取得文件类型信息,用法如下
1 | switch (sb.st_mode & S_IFMT) { |
而掩码S_IFMT
以及表示相应文件类型的S_IDBLK
等实际上是一些八进制位数的宏。
除了直接使用掩码来判断,更简单的方法是使用定义好的比较宏
1 |
|
而许可权限的信息同样可以通过相应的掩码来获取。
在获取之后这些信息之后,即可将其转换为对应的字符串类似drwxrwxr--
。
3.2 获取链接数、文件大小和时间信息
链接信息即保存在成员st_nlinks
里,直接打印即可。
文件大小信息即保存在成员st_size
里,同样直接打印即可。
时间信息即保存在成员st_mtime
里,需要注意的是这里需要使用ctime()
函数来将其转换成要求的格式来打印。
1 |
|
3.3 获取所有者和组名信息
首先需要知道Linux系统的用户信息保存在/etc/passwd
中,但这个文件又没有包括所有的用户,在一些网络计算机系统中,所有主机通过NIS来进行身份验证,本地只保存所有用户的一个自己以备离线操作。
这里可以通过getwuid()
来获取完整的用户列表,该调用会自动选择从/etc/passwd
还是NIS中获取信息,提高了程序的可移植性。
1 |
|
所以可以调用getpwuid()
来获取用户信息,打印用户名称。另外还有一种可能是,文件所有者已经搬走了,账号被删除,但这个文件还在,此时该函数将返回NULL。所以这里还需要检查返回值,而不能直接读取,否则可能出现段错误。
类似的使用getgrgid()
来获取组列表,用法大同小异
1 |
|
该函数也是有可能返回NULL的。
4 实现代码
1 |
|
将其编译并进行测试
1 | gcc ls_l.c -o ls_l |
实现完成。
5 结束语
本篇文章主要通过了实现ls -l
来练习stat()
相关函数以及struct stat
的使用,熟悉了如何获取文件详细信息,并将其转换成我们需要的格式。在文件mode信息获取中,使用到的关于掩码的宏较多,通过这次练习即使记不住也有个大概印象,下次再需使用时也可以通过查询相关帮助信息来完成编写。
本篇文章主要内容依然总结自《Unix\Linux编程实践教程》一书,下一篇依然会是编程练习,记录who
命令的实现。