《UNIX环境高级编程(第3版)》——1.4 文件和目录

1.4 文件和目录

1.文件系统
UNIX文件系统是目录和文件的一种层次结构,所有东西的起点是称为根(root)的目录,这个目录的名称是一个字符“/”。

目录(directory)是一个包含目录项的文件。在逻辑上,可以认为每个目录项都包含一个文件名,同时还包含说明该文件属性的信息。文件属性是指文件类型(是普通文件还是目录等)、文件大小、文件所有者、文件权限(其他用户能否访问该文件)以及文件最后的修改时间等。stat和fstat函数返回包含所有文件属性的一个信息结构。第4章将详细说明文件的各种属性。

目录项的逻辑视图与实际存放在磁盘上的方式是不同的。UNIX文件系统的大多数实现并不在目录项中存放属性,这是因为当一个文件具有多个硬链接时,很难保持多个属性副本之间的同步。这一点将在第4章讨论硬链接时理解得更明晰。
2.文件名
目录中的各个名字称为文件名(filename)。只有斜线(/)和空字符这两个字符不能出现在文件名中。斜线用来分隔构成路径名的各文件名,空字符则用来终止一个路径名。尽管如此,好的习惯还是只使用常用印刷字符的一个子集作为文件名字符(如果在文件名中使用了某些shell的特殊字符,则必须使用shell的引号机制来引用文件名,这会带来很多麻烦)。事实上,为了可移植性,POSIX.1推荐将文件名限制在以下字符集之内:字母(a~z、A~Z)、数字(0~9)、句点(.)、短横线(-)和下划线(_)。

创建新目录时会自动创建了两个文件名:.(称为点)和..(称为点点)。点指向当前目录,点点指向父目录。在最高层次的根目录中,点点与点相同。

Research UNIX System和某些早期UNIX System V的文件系统限制文件名的最大长度为14个字符,BSD版本则将这种限制扩展为255个字符。现今,几乎所有商业化的UNIX文件系统都支持超过255个字符的文件名。
3.路径名
由斜线分隔的一个或多个文件名组成的序列(也可以斜线开头)构成路径名(pathname),以斜线开头的路径名称为绝对路径名(absolute pathname),否则称为相对路径名(relative pathname)。相对路径名指向相对于当前目录的文件。文件系统根的名字(/)是一个特殊的绝对路径名,它不包含文件名。

实例
不难列出一个目录中所有文件的名字,图1-3是ls(1)命令的简要实现。

#include "apue.h"
#include <dirent.h>

int
main(int argc, char *argv[])
{
   DIR        *dp;
   struct dirent  *dirp; 

   if (argc != 2)
     err_quit("usage: ls directory_name");

   if ((dp = opendir(argv[1])) == NULL)
     err_sys("can't open %s", argv[1]);
   while ((dirp = readdir(dp)) != NULL)
     printf("%s\n", dirp->d_name); 

   closedir(dp);
   exit(0);
}

图1-3 列出一个目录中的所有文件

ls(1)这种表示方法是UNIX系统的惯用方法,用以引用UNIX系统手册中的一个特定项。ls(1)引用第一部分中的ls项。各部分通常用数字1~8编号,在每个部分中的各项则按字母顺序排列。在本书中始终假定你有自己所使用的UNIX系统的手册。

早期的UNIX系统把8个部分都集中在一本《UNIX程序员手册》(UNIX Programmer's Manual)中。随着页数的增加,现在的趋势是把这些部分分别安排在不同的手册中,例如用户手册、程序员手册以及系统管理员手册等。

一些UNIX系统用大写字母把某一部分手册进一步分成若干小部分,例如,AT&T[1990e]中的所有标准I/O函数都被指明位于3S部分中,例如fopen(3S)。另一些UNIX系统不用数字而是用字母将手册分成若干部分,如用C表示命令部分等。
现今,大多数手册都以电子文档形式提供。如果用的是联机手册,则可用下面的命令查看ls命令手册页:

man 1 ls

man -s1 ls
图1-3只打印一个目录中各个文件的名字,不显示其他信息,如果该源文件名为myls.c,则可以用下面的命令对其进行编译,编译结果是生成默认名为a.out的可执行文件中。

cc myls.c
历史上,cc(1)是C编译器。在配置了GNU C编译系统的系统中,C编译器是gcc(1)。其中,cc通常链接至gcc。
示例输出如下:

$ ./a.out /dev
.
..
cdrom
stderr
stdout
stdin
fd
sda4
sda3
sda2
sda1
sda
tty2
tty1
console
tty
zero
null
                    很多行未显示
mem
$ ./a.out /etc/ssl/private
can't open /etc/ssl/private: Permission denied
$ ./a.out /dev/tty
can't open /dev/tty: Not a directory

本书将以以下方式表示输入的命令及其输出:输入的字符以等宽粗体表示,程序输出则以上面所示的等宽字体表示。对输出的注释以中文宋体表示。输入之前的美元符号($)是shell的提示符,本书总是将shell提示符表示为$。

注意,myls程序列出的目录中的文件名不是以字母顺序列出的,而ls命令一般是按字母顺序打印目录项。

在这个20行的程序中,有很多细节需要考虑。

首先,其中包含了一个头文件apue.h。本书中几乎每一个程序都包含此头文件。它包含了某些标准系统头文件,定义了许多常量及函数原型,这些都将用于本书的各个实例中,附录B列出了这一头文件。

接下来,我们包含了一个系统头文件dirent.h,以便使用opendir和readdir的函数原型,以及dirent结构的定义。在其他一些系统里,这些定义被分成多个头文件。比如,在Ubuntu 12.04中,/usr/include/dirent.h声明了函数原型,并且包含bits/dirent.h,后者定义了dirent结构(真正存放在/usr/include/x86_64- linux-gnu/bits下)。

main函数的声明使用了ISO C标准所使用的风格(下一章将对ISO C标准进行更多说明)。

程序获取命令行的第1个参数argv[1]作为要列出其各个目录项的目录名。第7章将说明main函数如何被调用,程序如何存取命令行参数和环境变量。
因为各种不同UNIX系统目录项的实际格式是不一样的,所以使用函数opendir、 readdir和closedir对目录进行处理。

opendir函数返回指向DIR结构的指针,我们将该指针传送给readdir函数。我们并不关心DIR结构中包含了什么。然后,在循环中调用readdir来读每个目录项。它返回一个指向dirent结构的指针,而当目录中已无目录项可读时则返回null指针。在dirent结构中取出的只是每个目录项的名字(d_name)。使用该名字,此后就可调用stat函数(见4.2节)以获得该文件的所有属性。
程序调用了两个自编的函数对错误进行处理:err_sys和err_quit。从上面的输出中可以看到,err_sys函数打印一条消息(“Permission denied”或“Not a directory”),说明遇到了什么类型的错误。这两个出错处理函数在附录B中说明,1.7节将更多地叙述出错处理。

当程序将结束时,它以参数0调用函数exit。函数exit终止程序。按惯例,参数0的意思是正常结束,参数值1~255则表示出错。8.5节将说明一个程序(如shell或我们所编写的程序)如何获得它所执行的另一个程序的exit状态。
4.工作目录
每个进程都有一个工作目录(working directory),有时称其为当前工作目录(current working directory)。所有相对路径名都从工作目录开始解释。进程可以用chdir函数更改其工作目录。

例如,相对路径名doc/memo/joe指的是当前工作目录中的doc目录中的memo目录中的文件(或目录)joe。从该路径名可以看出,doc和memo都应当是目录,但是却不能分辨joe是文件还是目录。路径名/urs/lib/lint是一个绝对路径名,它指的是根目录中的usr目录中的lib目录中的文件(或目录)lint。

5.起始目录
登录时,工作目录设置为起始目录(home directory),该起始目录从口令文件(见1.3节)中相应用户的登录项中取得。

时间: 2024-12-22 15:51:15

《UNIX环境高级编程(第3版)》——1.4 文件和目录的相关文章

ubuntu-最近在学习Unix 环境高级编程,配置环境时遇到了些问题

问题描述 最近在学习Unix 环境高级编程,配置环境时遇到了些问题 最近再看APUE(UNix 环境高级编程)的第三版,照着教程在中配置环境.也就是想要运行书中的源码,则要安装 libbsd-dev包,而每次安装这个包时,都如上报错,请问各位大虾,该怎么解决呢? 解决方案 你好, 类似的问题我也遇到过 ubuntu下apt-get install安装软件, 报"无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系",今天终于找到解决方法了. 一般出现这种情况的原

Mac OS X 10.8 中编译APUE(Unix环境高级编程)的源代码过程_C 语言

最近在温习APUE(<unix环境高级编程>),以前都是在linux下搞,现在打算在自己机器弄下,于是google了下,把编译的事情搞定了,修改了一些教程的一些错误,比如下载链接之类的. 1.下载源文件,我这里是第二版,貌似第三版的英文版出来了... 复制代码 代码如下: wget http://www.apuebook.com/src.2e.tar.gz 2.解压 复制代码 代码如下: tar zxf src.2e.tar.gz 3.修改些东西 复制代码 代码如下: cd apue.2e/

UNIX环境高级编程---标准I/O库

前言:我想大家学习C语言接触过的第一个函数应该是printf,但是我们真正理解它了吗?最近看Linux以及网络编程这块,我觉得I/O这块很难理解.以前从来没认识到Unix I/O和C标准库I/O函数压根不是一码事.Unix I/O也叫低级I/O,也叫Unbuffered I/O,是操作系统内核部分,也是系统调用:而C标准I/O函数相对也成Buffered I/O,高级I/O,一般是为了效率考虑对这些系统调用的封装.以前使用getchar()经常为输入完后的回车而出错.那是不理解标准I/O实现时的

UNIX环境高级编程中的apue.h

/************** * *apueerror.h * *************/ #include <apue.h> #include <stdio.h> #include <errno.h> /* for definition of errno */ #include <stdarg.h> /* ISO C variable aruments */ static void err_doit(int, int, const char *, va

unix高级编程-UNIX环境高级编程 times() 疑问

问题描述 UNIX环境高级编程 times() 疑问 例程 int main(int argc, char *argv[]) { clock_t s_clk,e_clk; struct tms s_tms,e_tms; s_clk = times(&s_tms); system("ls /dev"); system("date"); sleep(1); e_clk = times(&e_tms); printf("e_clk %ld - s

unix环境高级编程-UNIX环境高级编程源代码对应

问题描述 UNIX环境高级编程源代码对应 今天开始学习UNIX环境高级编程,书中的源代码下载到了,但是发现根本不是按章节来的,找起来是相当的费时间,有哪位大神用过后知道他们的对应关系么,比如1-1对应ls1.c这样,真是万分感激,造福大家啊!

《UNIX环境高级编程(第3版)》——导读

前言 当Addison-Wesley公司的编辑找到我说想修订Rich的这本书时,我第一反应是这本书没有多少要改的.尽管13年过去了,Rich的书还是巍然屹立.但是,与当初本书出版的时候相比,今日的UNIX行业已经有了巨大的变化. 系统V的各个变种渐渐被Linux所取代.原来生产硬件配以各自的UNIX版本的几个主要厂商,要么提供了Linux的移植版本,要么宣布支持Linux.Solaris可能算是硕果仅存的占有一定市场份额的UNIX系统V版本4的后裔了. 加州大学伯克利分校的CSRG(计算机科学研

《UNIX环境高级编程(第3版)》——2.3 UNIX系统实现

2.3 UNIX系统实现 上一节说明了3个由各自独立的组织所制定的标准:ISO C.IEEE POSIX以及Single UNIX Specification.但是,标准只是接口的规范.这些标准是如何与现实世界相关连的呢?这些标准由厂商采用,然后转变成具体实现.本书中我们不仅对这些标准感兴趣,还对它们的具体实现感兴趣. 在McKusick等[1996]的1.1节中给出了UNIX系统家族树的详细历史.UNIX的各种版本和变体都起源于在PDP-11系统上运行的UNIX分时系统第6版(1976年)和第

《UNIX环境高级编程(第3版)》——2.5 限制

2.5 限制 UNIX系统实现定义了很多幻数和常量,其中有很多已被硬编码到程序中,或用特定的技术确定.由于大量标准化工作的努力,已有若干种可移植的方法用以确定这些幻数和具体实现定义的限制.这非常有助于改善UNIX环境下软件的可移植性. 以下两种类型的限制是必需的. (1)编译时限制(例如,短整型的最大值是什么?) (2)运行时限制(例如,文件名有多少个字符?) 编译时限制可在头文件中定义.程序在编译时可以包含这些头文件.但是,运行时限制则要求进程调用一个函数获得限制值. 另外,某些限制在一个给定