目录
一、前言
二、扫描目录
1、opendir
2、readdir
3、telldir
4、seekdir
5、 closedir
6、A Directory-Scanning Program
三、Errors
1、strerror
2、perror
一、前言
本文将简单介绍Linux文件和目录,以及如何操作它们(如何创建文件、打开、读、写和关闭,程序如何操作目录,如创建、扫描和删除目录等)。本文大部分内容将会介绍处理文件和目录的各种调用,涵盖了与文件相关的部分主题:(1)文件和设备;(2)系统调用;(3)库函数;(4)低级文件访问;(5)管理文件;(6)标准I/O库;(7)格式化的输入和输出;(8)文件和目录维护;(9)扫描目录;(10)错误;(11)/proc文件系统;(12)fcntl 和 mmap。这些内容本博客不会全部介绍,只是进行部分的基础介绍。
二、扫描目录
Linux 系统上的一个常见问题是扫描目录,也就是说,确定驻留在特定目录中的文件。
在 shell 程序中,这很简单——只要让 shell 展开通配符表达式即可。过去,不同的 UNIX 变体都允许对低级文件系统结构进行编程访问。我们仍然可以将目录作为常规文件打开并直接读取目录条目,但是不同的文件系统结构和实现使得这种方法不可移植。现在已经开发了一套标准的库函数,使目录扫描更加简单。
目录函数的头文件是 dirent.h,它们使用一个结构 DIR 作为目录操作的基础。指向这个结构的指针称为目录流 (DIR *),其作用与文件流 (file *) 用于常规文件操作的方式非常相似。目录条目本身以 dirent 结构返回,也在 dirent.h 中声明,因为不应该直接更改 DIR 结构中的字段。
我将复习以下函数:
❑ opendir, closedir
❑ readdir
❑ telldir
❑ seekdir
❑ closedir
1、opendir
opendir 函数的作用是:打开目录并建立目录流。如果成功,它将返回一个指向 DIR 结构的指针,用于读取目录条目。
opendir 失败时返回空指针。注意,目录流使用低级文件描述符来访问目录本身,因此如果打开的文件太多,opendir 可能会失败。
2、readdir
readdir 函数返回一个指向结构的指针,该结构详细描述了目录中的下一个目录条目流 dirp。连续调用 readdir 返回更多的目录条目。如果出现错误,并且在目录的末尾,readdir 返回 NULL。兼容 posix 的系统在目录末尾返回 NULL 时保留 errno 不变,并在发生错误时设置它。
注意,如果同时有其他进程在该目录中创建和删除文件,则 readdir 扫描不能保证列出该目录中的所有文件(和子目录)。包含目录条目详细信息的 direct 结构包括以下条目:
要确定目录中文件的进一步细节,需要调用 stat。
3、telldir
telldir 函数返回一个值,该值记录目录流中的当前位置。我们可以在后续调用 seekdir 时使用此命令将目录扫描重置到当前位置。
4、seekdir
seekdir 函数在 dirp 给出的目录流中设置目录入口指针。用于设置位置的 loc 值应该已经从之前对telldir 的调用中获得。
5、 closedir
closedir 函数关闭目录流并释放与之关联的资源。成功时返回 0,出错时返回 -1。
在下一个程序 printdir.c 中,我们将把许多文件操作函数组合在一起,以创建一个简单的目录列表。目录中的每个文件单独列在一行上。每个子目录的名称后面都有一个斜杠,其中列出的文件缩进 4 个空格。该程序将一个目录更改为子目录,以便它找到的文件具有可用的名称,也就是说,它们可以直接传递到 opendir。程序在嵌套很深的目录结构上会失败,因为打开的目录流的数量是有限制的。
当然,我们可以通过使用命令行参数来指定起点,从而使其更通用。查看诸如 ls 这样的实用程序的 Linux 源代码,并找到关于更通用实现的想法。
6、A Directory-Scanning Program
从适当的头文件开始,然后是 printdir 函数,它打印出当前目录。它将使用缩进的深度参数递归子目录。
#include<unistd.h>
#include<stdio.h>
#include<dirent.h>
#include<string.h>
#include<sys/stat.h>
#include<stdlib.h>
void printdir(char *dir,int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp=opendir(dir))==NULL){
fprintf(stderr,"cannot open directory:%s \n",dir);
return ;
}
chdir(dir);
while((entry=readdir(dp))!=NULL){
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)){
/* Found a directory, but ignore . and .. */
if(strcmp(“.”,entry->d_name)==0||strcmp(“..”,entry->d_name) == 0)
continue;
printf("%*s%s/\n",depth,"",entry->d_name);
/* Recurse at a new indent level */
printdir(entry->d_name,depth+4);
}
else printf("%*s%s\n",depth,"",entry->d_name);
}
chdir("..");
closedir(dp);
}
int main()
{
printf("Directory scan of /home:\n");
printdir("/home",0);
printf("done.\n");
exit(0);
}
该程序扫描主目录并产生如下的输出。要查看其他用户的目录,可能需要超级用户权限。
当然该代码还可以改进一些,但这里不再进行讨论。
三、Errors
许多系统调用和函数都可能由于许多原因而失败。当出现这种情况时,它们通过设置外部变量 errno 的值来指示失败的原因。许多不同的库使用此变量作为报告问题的标准方法。值得重复的是,程序必须在给出问题的函数之后立即检查 errno 变量,因为它可能被调用的下一个函数覆盖,即使该函数本身没有失败。
错误的值和含义列在头文件 errno.h 中。它们包括:
有两个有用的函数可以在发生错误时报告错误: strerror 和 perror。
1、strerror
strerror 函数将错误号映射为描述已发生错误类型的字符串。这对于记录错误条件很有用。
2、perror
perror 函数还将 errno 中报告的当前错误映射为字符串,并将其打印到标准错误流中。它的前面是字符串 s 中给出的消息(如果不是NULL),后面是冒号和空格。
以上,Linus 文件处理(三)
祝好