MySQL, Oracle, Linux, 软件架构及大数据技术知识分享平台

网站首页 > linux / 正文

Linux编程入门(5)-读取目录

2024-11-26 16:46 huorong linux 5 ℃ 0 评论

学习目标

通过分析 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 的使用者有意义。

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

总结,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()

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

Tags:linux 上级目录

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言