红联Linux门户
Linux帮助

在UNIX下对文件与目录进行编程

发布时间:2008-04-15 16:44:25来源:红联作者:Ttapocs
近来,随着UNIX系统的不断普及,使用UNIX系统的单位和个人也越来越多,特别是Linux的推出,更是风靡全球。基于UNIX系统的编程也愈显重要。本文以一个简单的程序为例,说明怎样在UNIX系统下对文件和目录进行编程。该例是一个类似于UNIX系统命令ls的程序,用于列出指定的目录中所有文件和子目录并计算出指定目录所用的磁盘空间。
UNIX文件系统简介
文件是通过操作系统来管理的。文件的结构以及命名、存取、使用、保护和实现方法都是UNIX系统中的重要内容。总体上,UNIX系统中处理文件的那部分称为文件系统。同DOS类似,UNIX中的文件系统被组织成树状结构。
UNIX中的文件和目录通过一个9比特的保护码来进行保护。保护码分成三个3比特的域,分别对应着文件主、同组用户和其他用户。每个域有一位标识读权限,一位标识写权限,一位标识执行权限,这三位即rwx位。
在UNIX文件系统中通常包含如下文件类型:
普通文件:一个文件包含所有用户放在其中的信息。它可以被认为是一个字节序列。它与DOS及WINDOWS系统不同,对于每个文件都具有一定的读写权限。
目录:目录提供了文件名称与文件自身之间的映射,由此使得文件系统上的一个结构被作为一个整体对待。一个目录可包含文件,也可以包含子目录,这些子目录同样可以包含更多的文件和子目录。一个目录在被读的时候,它的行为完全像一个普通文件,但它不能被没有权限的用户程序访问。
特殊文件:特殊文件是UNIX文件系统中最有特色的地方之一。每个I/O设备(磁盘驱动器、终端等)都与一个这样的文件有关。对特殊文件的操作和普通文件一样,但它引发了对相关设备的操作。特殊设备文件的实体保存在目录/dev中。
符号链接文件:一个符号链接在行为上就像指向另一个文件的指针,这有点像C语言中的指针。在文件系统中,实现这一点是建立一个带有链接名称的文件,该链接指向文件的路径名。

I 标识号,I列表和I节点:一个目录是由一系列结构组成的,每个结构包含有一个文件名和一个指向文件自身的指针,该指针是一个整数,称为文件的I标识号。当文件被访问时,它的I标识号用来作为索引打开一个系统表(I列表),系统表中存放着文件(I节点)的实体。I节点中包含了如下对文件的描述信息:
.文件自身的用户和用户组ID
.文件的保护码
.文件内容所在的物理磁盘地址
.文件的大小
.最后一次I节点改变的时间,最后一次使用和最后一次修改的时间
.连接该文件的次数,即它出现在其它目录中的次数
.一个指明文件类型的标记(目录、普通文件或特殊文件)
有关的系统调用介绍
lstat系统调用:这是一个非常有用的系统调用,用来获取存储在一个I节点上的信息。它接受两个参数:一个字符串指针,指向说明一个文件的路径名;另一个是指向stat结构的指针,在这个结构中,存放着有关这个文件的信息。Stat结构包括以下成员:
st_mode:这个字段包含文件类型和它所具有的访问权限
st_ino:这个字段在一个给定的文件系统中唯一的标识了这个文件
st_dev:这个字段唯一地标识了包括这个文件的文件系统
st_rdev:如果I节点是一个特殊设备文件,则这个字段标识设备的类型
st_nlink:文件链接的个数
st_uid:文件属主的用户ID
st_gid:文件的用户组的组ID
st_size:文件的字节数
st_atime:文件数据最近一次被访问的时间
st_mtime:文件数据最近一次被更改的时间
st_ctime:文件状态最近一次被修改的时间
opendir:它打开一个指定的目录,并返回一个DIR类型的指针。DIR数据类型包含如下两个元素:
d_ino:文件I标识号,如果文件被删除,则d_ino为0
d_name:此目录下包含的文件名
readdir:它读出指定目录中的一个文件或子目录,并将指针移到下一个文件或子目录。
closedir:关闭相应的目录。
程序说明
该程序是一个类似于UNIX命令ls 的小工具(取文件名为lx,为了简单起见,此程序只读取了文件的字节数,即st_size)。它可以接受一个参数:指定的目录;也可以不带参数,则默认为当前目录。程序列出指定目录及其子目录中所包含的所有文件的文件名和文件的字节数,并计算出指定目录所占磁盘空间的大小。程序详细说明如下(程序清单见附录):
1-5行:包含在程序中要用到的一些头文件。
6行:定义常数TABSPACES为TAB键所跳过的空格数,用于规整后面显示语句的屏幕格式。
11行:定义一个变量dir_size用于存放指定目录所占磁盘空间的大小。
12、13行:判断命令行如果没有带参数,则调用list函数,将指定目录置为当前目录,并将返回的目录大小存入变量dir_size中。
14-18行:如果命令行带的参数大于一个,则在屏幕上显示出此命令的简要语法并退出程序。
19、20行:如果命令行只带一个参数,则将此参数作为指定目录调用list函数,同时也返回目录的大小并存入变量dir_size中。
21行:打印出指定目录及其子目录所占磁盘空间的大小。
23行:定义list函数,该函数采用递归法列出字符串name所指定的目录及其子目录中所包含的文件名和文件的大小,并计算name所指定的目录所占用的磁盘空间。
26-32行:定义函数中要用的变量并赋初值。
33行:打印当前所操作目录的目录名。
34行:开始一个循环次数为2的for循环。第1次循环找出目录中包含的所有文件,第2次循环找出目录中包含的所有子目录。
36-40行:打开指定的目录,如果打开成功则返回指向此目录的指针,否则返回NULL。程序打印出出错信息并退出。
41行:开始一个while循环,每循环一次读出目录中的一个文件或子目录,直到读出目录中所有的文件或子目录,readdir返回NULL表示已读到此目录的尾部。
43、44行:判断读出的文件的I标识号,如果为0,则表示该文件已被删除。程序继续执行并读取下一个文件。
45-47行:在读出的文件名前加上绝对或相对路径,以使程序能正确的从文件系统中访问指定的文件。
48-52行:调用lstat获取指定文件的信息,将获取的信息存储到结构sbuf中。如果出错则退出程序。
53行:判断读出的文件是否是一个目录,这个条件用语句(sbuf.st_mode&S_IFMT)==S_IFDIR实现。其它三个条件用于滤除符号链接、目录本身和上一级目录,以避免出现死循环。
55-60行:如果是第2次for循环则递归调用list函数,列出相应的子目录,同时累加目录所占磁盘空间的大小。
63-71行:如果是第1次for循环则打印出文件名和文件所占用的字节数,同时累加所占磁盘空间的大小。用三条打印语句是因为文件名长度不一致,为了打印出的屏幕美观。
75行:关闭相应的目录。
77行:返回本目录所占用的磁盘空间。
本程序在Turbo Linux 4.0和FreeBSD 3.0上调试运行通过。
参考资料:《UNIX系统程序员进阶》 林新观编著 清华大学出版社
《UNIX系统V/386第4版程序员参考手册》 电子工业出版社
文章评论

共有 1 条评论

  1. Ttapocs 于 2008-04-15 16:44:31发表:

    附录:程序lx.c
    1 #include
    2 #include
    3 #include
    4 #include
    5 #include
    6 #define TABSPACES 8
    7 main(argc,argv)
    8 int argc;
    9 char **argv;
    10 {
    11 off_t dir_size;
    12 if (argc<2)
    13 dir_size=list(".");
    14 else if (argc>2)
    15 {
    16 printf("Usage: lx [path]\n");
    17 exit(1);
    18 }
    19 else
    20 dir_size=list(argv[1]);
    21 printf(" This directory size is %i bytes.\n",dir_size);
    22 }
    23 off_t list(name)
    24 char *name;
    25 {
    26 char pn[255];
    27 DIR *dp;
    28 off_t f_size,d_size;
    29 int i;
    30 struct stat sbuf;
    31 struct direct *dir;
    32 f_size=0;
    33 printf("Current directory is %s\n",name);
    34 for (i=0;i<=1;i++)
    35 {
    36 if ((dp=opendir(name))==NULL)
    37 {
    38 perror(name);
    39 exit(1);
    40 }
    41 while ((dir=readdir(dp))!=NULL)
    42 {
    43 if(dir->d_ino==0)
    44 continue;
    45 strcpy(pn,name);
    46 strcat(pn,"/");
    47 strcat(pn,dir->d_name);
    48 if (lstat(pn,&sbuf)<0)
    49 {
    50 perror(pn);
    51 exit(1);
    52 }
    53 if
    (((sbuf.st_mode&S_IFMT)!=S_IFLNK)&&((sbuf.st_mode&S_IFMT)==S_IFDIR)&&(strcmp(dir->d_name,".")!=0)&&(strcmp(dir->d_name,"..")!=0))
    54 {
    55 if (i==1)
    56 {
    57 d_size=list(pn);
    58 f_size=f_size+d_size;
    59 }
    60 }
    61 else
    62 {
    63 if (i==0)
    64 {
    65 f_size=f_size+sbuf.st_size;
    66 if (strlen(dir->d_name)>=2*TABSPACES)
    67 printf("%s\t%i\n",dir->d_name,sbuf.st_size);
    68 else if (strlen(dir->d_name)>=TABSPACES)
    69 printf("%s\t\t%i\n",dir->d_name,sbuf.st_size);
    70 else
    71 printf("%s\t\t\t%i\n",dir->d_name,sbuf.st_size);
    72 }
    73 }
    74 }
    75 closedir(dp);
    76 }
    77 return f_size;
    78 }