Linux编程入门(5)-读取目录(ls 初步实现)

学习目标

通过分析 ls 指令,来学习 Linux 目录和文件属性相关的知识。

代码实验环境

操作系统:Ubuntu 18.04 LTS

编译器 gcc 版本:gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0

ls 指令介绍

ls 指令可以列出目录中所有文件的名字,以及这些文件的其他信息。

在命令行输入 ls:

$ ls
bin    etc             lib         media  root  srv       usr
boot   home            lib32       mnt    run   swapfile  var
cdrom  initrd.img      lib64       opt    sbin  sys       vmlinuz
dev    initrd.img.old  lost+found  proc   snap  tmp       vmlinuz.old

ls 的默认动作是找出当前目录中所有文件的名字,并按字典序排列后输出。

ls 还能显示其他信息,如果加上 -l 选项,ls 会列出每个文件的详细信息,也叫 ls 的长格式:

$ ls -l
total 2097260
drwxr-xr-x   2 root root       4096 9月  26 16:19 bin
drwxr-xr-x   3 root root       4096 11月 10 09:11 boot
drwxrwxr-x   2 root root       4096 4月  18  2021 cdrom
drwxr-xr-x  18 root root       4180 11月 16 22:57 dev
drwxr-xr-x 147 root root      12288 11月 16 22:58 etc
drwxr-xr-x   3 root root       4096 6月  22 14:43 home
lrwxrwxrwx   1 root root         34 11月  9 10:21 initrd.img -> boot/initrd.img-4.15.0-162-generic
lrwxrwxrwx   1 root root         34 11月  9 10:21 initrd.img.old -> boot/initrd.img-4.15.0-161-generic
drwxr-xr-x  22 root root       4096 4月  18  2021 lib
drwxr-xr-x   2 root root       4096 4月  18  2021 lib32
drwxr-xr-x   2 root root       4096 4月  18  2021 lib64
drwx------   2 root root      16384 4月  18  2021 lost+found
drwxr-xr-x   4 root root       4096 4月  21  2021 media
drwxr-xr-x   2 root root       4096 4月  27  2018 mnt
drwxr-xr-x   3 root root       4096 10月 29 13:08 opt
dr-xr-xr-x 240 root root          0 11月 16 22:57 proc
drwx------   7 root root       4096 5月  21 09:39 root
drwxr-xr-x  31 root root        960 11月 16 22:58 run
drwxr-xr-x   2 root root      12288 11月 16 22:58 sbin
drwxr-xr-x  15 root root       4096 9月  29 08:53 snap
drwxr-xr-x   2 root root       4096 4月  27  2018 srv
-rw-------   1 root root 2147483648 4月  18  2021 swapfile
dr-xr-xr-x  13 root root          0 11月 16 23:02 sys
drwxrwxrwt  14 root root       4096 11月 16 23:03 tmp
drwxr-xr-x  15 root root       4096 4月  18  2021 usr
drwxr-xr-x  15 root root       4096 8月  13 17:37 var
lrwxrwxrwx   1 root root         31 11月  9 10:21 vmlinuz -> boot/vmlinuz-4.15.0-162-generic
lrwxrwxrwx   1 root root         31 11月  9 10:21 vmlinuz.old -> boot/vmlinuz-4.15.0-161-generic

每一行代表一个文件和它的多个属性。

Linux 系统中会有很多的目录,每个目录中又会有很多文件。如果要列出一个非当前目录的内容或者是特定文件的信息,则需要在参数中给出目录名或文件名。

例子说明
ls /tmp列出 /tmp 目录中各文件名
ls -l docs列出 docs 目录中各文件的属性
ls -l …/Makefile显示文件 …/ Makefile 的属性
ls *.c显示与 *.c 匹配的文件

如果参数是目录,ls 列出目录的内容;如果参数是文件,ls 列出文件名和属性。

ls 经常用到的命令行选项

命令说明
ls -a列出包含以 “.” 开头的文件
ls -lu显示最后访问时间
ls -s显示以块为单位的文件大小
ls -t按时间排序输出
ls -F显示文件类型

在 Linux 系统中,ls 一般不会列出以 “.” 开头的文件,可以把这样的文件看作是隐藏文件。 ls 加上 -a 选项后,遇到这样的文件也必须把它们列出来。对操作系统来说,文件名前面的 “.” 没有任何特殊含义,它只对 ls 的使用者有意义。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iDV2U70N-1639474793394)(M:\document\03.Linux\02.Linux编程入门\05.Linux编程入门(5)]-读取目录(ls 初步实现)_20211117\image-20211117003403543.png)

某些应用程序的配置文件以 “.” 开头,这是由习惯形成的,因为在大多数情况下可以将他们隐藏。需要时可以直接打开编辑,不需任何特殊的操作。

总结,ls 做了以下两件事:

  • 出目录的内容

  • 显示文件的信息

注意,ls 对文件和目录所作的操作是不同的。ls 能根据参数判断出是文件还是目录。这是如何做到的呢?有以下三点内容需要搞清楚:

  • 如何列出目录的内容
  • 如何读取并显示文件的属性
  • 如何根据名字判断出,它是目录还是文件

ls 工作原理

ls 指令会显示一个文件名的列表,大致的工作流程如下:

打开目录 --> 读取目录 --> 显示文件信息 --> 关闭目录

目录是什么

目录是一种特殊的文件,它的内容是文件和目录的名字。它包含很多记录,每条记录格式由统一的标准定义,其内容代表一个文件或者目录。

目录文件永远不会空,每个目录至少包含两个特殊的项—— “.” 和 “…”。其中,“.” 代表当前目录,“…” 代表上一级目录。

如何读取目录内容

从目录中读取数据与从文件读数据是类似的。首先打开一个目录,然后读取目录项,最后关闭目录。

opendir()系统函数可以打开一个目录,并返回指向该目录的句柄,供后续调用使用。函数原型如下:

#include <sys/types.h>
#include <dirent.h>

DIR *opendir(const char *name);

参数 name,为目录名字。

打开成功则返回指向 DIR 类型结构的指针,该结构也就是所谓的目录流,调用者可将该指针传递给其他相关函数。若打开失败,则返回 NULL。

从 opendir() 返回的指针,指向目录列表的首条记录。

readdir() 系统函数读取目录项,函数返回一个指向目录当前记录的指针。函数原型如下:

#include <dirent.h>

struct dirent *readdir(DIR *dirp);

参数 dirp,为 opendir() 调用返回的指针。

调用成功,则返回一个指向 struct dirent 类型的指针。调用失败或者读取到目录末尾,则返回 NULL。

调用readdir() 一次,就会从 dirp 所指代的目录流中读取下一条目录项。struct dirent 结构由静态分配而得到,每读取一次目录项,都会覆盖该结构。

struct dirent 结构定义如下:

struct dirent 
{
    ino_t          d_ino;       /* i节点号 */
    off_t          d_off;       
    unsigned short d_reclen;    /* 记录信息长度 */
    unsigned char  d_type;      /* 文件类型; 并非所有文件系统类型支持此字段 */
    char           d_name[256]; /* 文件名称 */
};

readdir() 返回时,并未对文件进行排序,而是按照文件在目录中出现的天然次序。这取决于文件系统向目录添加文件时所遵循的次序,以及其在删除文件后对目录列表中空隙的填充方式。

close() 系统函数将关闭打开的目录,同时释放目录流所使用的资源。其函数原型如下:

#include <sys/types.h>
#include <dirent.h>

int closedir(DIR *dirp);

参数 dirp,为 opendir() 调用返回的指针。

调用成功,则返回 0。否则返回 -1。

ls 初步实现

经过上面的分析,我们编写代码读取目录内容,实现简单的 ls 命令。

#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>

void do_ls(char *dirname);

int main(int ac, char *av[])
{
	if(ac == 1)
	{
		/* 未指定目录参数,默认动作是找出当前目录中所有文件的名字 */
		do_ls(".");
	}
	else
	{
		/* 逐个显示指定目录的内容 */
		while(--ac)
		{
			printf("%s:\n", *++av);
			do_ls(*av);
		}
	}

	return 0;
}

/* 读取并显示目录中的文件名 */
void do_ls(char *dirname)
{
	DIR *dir_ptr = NULL;
	struct dirent *direntp = NULL;

	/* 打开目录,并判断返回值 */
	if((dir_ptr = opendir(dirname)) == NULL)
	{
		/* 打开失败,显示失败信息 */
		fprintf(stderr, "ls1: cannot open %s\n", dirname);
	}
	else
	{
		/* 打开成功,读取目录项目,并显示 */
		while((direntp = readdir(dir_ptr)) != NULL)
		{
			printf("%s\n", direntp->d_name);
		}
		
		/* 关闭打开的目录 */
		closedir(dir_ptr);
	}
}

将上面代码编译运行,结果如下

$ gcc -o ls1 ls1.c    /* 编译 */

$ ./ls1       /* 未携带参数 */
.
ls1
ls1.c
..

$ ./ls1 /      /* 显示根目录内容 */
/:
cdrom
dev
sbin
.
tmp
sys
proc
lib
vmlinuz
root
etc
lost+found
lib64
initrd.img.old
var
lib32
home
mnt
run
opt
boot
usr
snap
..
swapfile
media
srv
bin
initrd.img
vmlinuz.old

再看看标准 ls 指令执行结果

$ ls
ls1  ls1.c

$ ls /
bin    etc             lib         media  root  srv       usr
boot   home            lib32       mnt    run   swapfile  var
cdrom  initrd.img      lib64       opt    sbin  sys       vmlinuz
dev    initrd.img.old  lost+found  proc   snap  tmp       vmlinuz.old

对比分析可知。我们编写的程序可以正常显示目录内容,但还有需要改进的地方:

  • 排序

    ls1 的输出没有经过排序。可以将所有的文件名读入到一个数组中,并用 qsort 函数把数组进行排序。

  • 分栏

标准的 ls 输出是分栏排列的,有些是以行排序输出,有些是以列排序输出。可以先把文件名读入数组,然后计算出列的宽度和行数。

  • “.” 和 “…” 文件

ls1 列出了 “.” 和 “…” 文件,而标准 ls 只有在给出 -a 选项时才会列出这些文件。可以使 ls1 能够接收选项 -a,并在没有 -a 的时候不显示隐藏文件。

  • 选项 -l

ls1 不会处理 -l 选项。标准 ls 会处理选项 -l ,并显示出文件的详细信息。要解决这个问题不太容易,因为 dirent 结构中没有提供所需要的信息,如文件大小、文件所有者等。

小结

经过上述内容,我们学习了 ls 指令能够做什么,以及其是如何工作的。并通过编写代码,实现简单的 ls 功能 显示目录内容。

学习的系统函数有:

  • opendir()
  • readdir()
  • closedir()

本篇文章只是读取了目录内的文件名,那如何获取文件的详细信息呢?下篇继续分析。

————————————————————————

关注公众号【一起学习嵌入式】,后台回复 “ linux ”,获取经典Linux书籍资料
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zsky_01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值