Linux 版 (精华区)

发信人: netiscpu (说不如做), 信区: Linux
标  题: 仅仅列出连接:只能如此
发信站: 哈工大紫丁香 (Tue May 18 08:35:17 1999), 转信

《Linux公报》……让Linux更富魅力!

---------------------------------------------------------------------------

-----

 The Answer Guy 

By James T. Dennis, answerguy@ssc.com

Starshine Technical Services, http://www.starshine.org/

翻译: gaia

---------------------------------------------------------------------------

-----

仅仅列出连接:只能如此

 From Jerry Giles on Thu, 05 Nov 1998 

很抱歉打搅你,但我在浏览Linux信息时碰巧看到了你的名字.我现在参加本地学校 

的一个CIS项目,最近的一次测验中的一道题我始终想不出答案.教授问我们什么命 

令能只列出一个目录里"被连接的文件".我猜他是期望我们用ls命令的某些选项,但 

我找遍ls所有的选项也没发现能有这种功能的.你能帮我吗? 

谢谢,jerry giles 

要么是你理解错了,要么是你的教授说的很不严谨.'ls'命令就是用来"列出连接"的 

---所有的目录项都是连接!其中一些是符号连接,另一些是"硬"连接(就是通常被认 

为是"普通"目录项的).'ls'命令所做的只能是列出连接.我能列出的其它信息是它从 

每个连接所指向的inode中取出来的(用stat()函数). 

所以,照你这么问这个问题根本没有意义. 

那么,如果问题是想列出符号连接,倒是有几个能说的通的简单回答. 

ls -l | grep ^l 

...这样把所有连接(硬的和"符号的")的"长"列表过滤一遍,只显示那些以字母 

"l"开头的.在一个"长"目录列表中,第一段字符(或者叫域)是用来表示目录连接所 

指向的那个文件的类型和权限.(l即符号连接,d是目录,s为socket,p是"FIFO/有名 

FIFO管道",b和c分别是"块"和"字符"特殊设备节点---通常只在/dev/目录下---而 

"-"为"普通"文件). 

长列表的第二个域是"连接数".它告诉你有多少硬连接指向了同一个inode. 

以下例子是我自己的根目录 

drwxr-xr-x  14 root     root         1024 Sep 27 17:19 . 

drwxr-xr-x  14 root     root         1024 Sep 27 17:19 .. 

-rw-r--r--   2 root     root       219254 Sep 27 17:19 System.map 

drwxr-xr-x   2 root     root         2048 Sep 12 03:25 bin 

drwxr-xr-x   2 root     root         1024 Sep 27 17:20 boot 

drwxr-xr-x   2 root     root         1024 Aug 31 06:40 cdrom 

drwxr-xr-x  21 root     root         4096 Nov  4 03:12 etc 

lrwxrwxrwx   1 root     root           15 Apr 20  1998 home -> /usr/local/h

ome drwxr-xr-x   5 root     root         2048 Sep 16 23:48 lib 

drwxr-xr-x   2 root     root        12288 Mar 10  1998 lost+found 

drwxr-xr-x   9 root     root         1024 Aug 31 06:40 mnt 

lrwxrwxrwx   1 root     root           14 Mar 31  1998 opt -> /usr/local/op

t dr-xr-xr-x  63 root     root            0 Oct 13 02:25 proc 

drwx--x--x  13 root     root         2048 Oct 31 17:47 root 

drwxr-xr-x   5 root     root         2048 Sep 16 23:48 sbin 

drwxrwxrwt   8 temp     root         3072 Nov  5 09:33 tmp 

drwxr-xr-x  30 root     root         1024 Aug 31 13:32 usr 

lrwxrwxrwx   1 root     root           13 Aug 31 06:40 var -> usr/local/var

 -rw-r--r--   1 root     root       732668 Sep 27 17:19 vmlinuz 

这是用'ls -al /'产生的 

第二个域的数字(每行第一个数字)就是"连接数".这是指向同一个inode的硬连接 

的数目.这样我的根目录有14个指向它的连接.每个子目录的".."项都指向它.就是 

说,/usr/..指向/ ,同样/ect/.. ,/dev/..和所有在/下面一级的目录都是如此 

./usr/local/..指向/usr/,照此类推. 

我们可以看到'System.map'的连接计数为2.这意味着这个文件还有另一个名字.在 

这个文件系统内还有一个硬连接指向它. 

多数Unix新手都认为'ls'命令用来列出文件.这不对.'ls'命令列出的是到文件的连 

接.当你给'ls'加上像'-l'这样的参数时,你除了列出连接,还有它们指向的文件的 

一些信息.(在幕后,'ls'命令为每个目录项调用stat()函数).Unix/Linux的目录由 

文件名和inode的列表组成.所有与文件相关的其它信息都储存在inode里(如文件的类 

型,所有者,权限,连接数,三个时间/日期戳,大小---和最重要的---储存着文件内容 

的块(block)的清单) 

为了更好地理解这种不同,创建一个子目录(~/tmp/experiment).在里面随意放几个 

连接(用'ln'产生"硬连接",用'ln -s'做符号连接,并可以用'cp'命令拷贝一些文件 

进去).再用'chmod'命令除去你自己的对目录的执行权限. 

('chmod a-x ~/tmp/experiment'). 

*(从技术上说这只是一个"演示"而非"实验".不过这么说有点过分严谨了,只是顺便 

提一句而已. ) 

你应该能使用'ls'命令(确认用的是真正的'ls'命令---不是别名(alias),shell函数或

 shell脚本).这个命令应该正常发挥作用(如果不行---你可能把'ls'变成了'ls 

--color'或其它什么的别名---那就使用命令/bin/ls,或者在实验期间用'unalias 

ls'命令去掉'ls'的别名意义.一旦你可以使用'ls'命令了,不加任何参数可以得到 

"~/tmp/experiment"目录下文件名的列表,接着试试'ls -l'和'ls -i'. 

你会得到一长串"Permission denied"信息(当然你不是以root登录).并且注意你只 

能在目录外面使用这些命令.用'cd'命令进入目录要求你拥有目录的"执行"权限. 

之所以你会得到这些"Permission denied"信息是因为,为了得到关于文件的任何信 

息(除了连接名以外),'ls'命令必须访问inode(这需要对目录的执行权).对这个目 

录你可以使用'ls'或'ls -a'命令,因为它们只提供了连接名的列表.这类命令不需 

要访问储存在inode里的文件信息. 

所以,既然你已经理解了连接是什么(希望如此)---你就能理解有关'rm'命令的一些 

知识. 

'rm'并不删除文件.'rm'删除到文件的连接.文件系统会检查连接数.如果它是 

"0",(并且没有打开的文件描述符,即没有进程打开这个文件),这个文件将被真正地 

删除. 

注意这里重要的一点:文件删除操作是间接完成的,是作为文件系统日常运行的一部 

分.'rm'和类似命令只不过调用"unlink()"(一个系统调用). 

这里还有一种可能.如果我打开一个文件(比如,用编辑器),然后用'rm'删除了这个 

文件,那会发生什么?(假设到文件只有一个硬连接) 

没什么特别的.连接计数为0但文件被打开了.只要文件被打开着,文件系统的维护例 

程就不去管这个文件的inode和数据块.一旦文件关闭了,这些例程将检测到连接数 

为0,然后删除这个文件.如果好多进程打开这个文件---那么只有在所有进程关闭文 

件后它才会被真正删除. 

实际上删除包括好几步.先是所有分配给这个文件的数据块(block)被重新分配给 

"free list".你可以把空闲链表(free list)想成是拥有磁盘上所有空闲空间的 

"特殊文件".具体的实现随文件系统的不同而不同.然后文件的inode被标记为"已删 

除"或被"清零".(这是随文件系统和版本而异的) 

现在,回到你原来的问题: 

找出一个目录下所有符号连接的更正规的办法是用'find'命令.试试以下命令: 

find / -type l -maxdepth 1 -print 

...GNU版本的'find'缺省使用-print操作所以在Linux下可以把它省略. 

"maxdepth 1"这部分是为了防止'find'查找整个文件树(注意:我倾向于使用"文件 

树"(file tree)或"文件层次系统"(file hiearchy)来指某个搜索起始点下所有文 

件*及所有mount上的文件系统*,而用"文件系统"来指单个被mount上的文件系统内所有

 文件.这是一个容易混淆之处). 

现在,如果问题是"找出所有连接数大于1的普通文件"你可以用: 

find ... -type f -maxdepth 1 -links +1 

其中省略号部分为一个或多个目录名/文件名,其他参数检查各种条件是否满足(当 

然要防止查找整个文件树).在GNU find里,许多数字条件可以用+x,x,-x来指定,---这 

里+x表示"大于'x'",而-x表示"小于'x'",x表示"等于x."这是find命令的妙处 

之一. 

我能想到的对这个问题的最后一种理解是:找到指向某一指定文件(inode)的所有连 

接.要做到这一点你要从inode着手.如果它不是一个目录,(*)并且其连接数大于一 

,那么搜索整个文件系统找出匹配其inode的所有其它连接.这对于刚接触Unix的学 

生可不是个简单问题.这需要写一个脚本. 

*(我们没必要搜索目录的其它硬连接,因为它们应该都在./*/..--就是说这些硬连 

接都是当前目录及下一级目录中的"."或".." .如果你想用自己写的代码强制建立 

指向一个目录的另外的硬连接---fsck可能会修正目录结构中的这种反常.一些版本 

的Unix历史上允许root(超级用户)创建到目录的硬连接---但Linux下的GNU工具不 

允许---所以你不得不自己写代码,或者不得不用一个hex编辑器直接修改文件系统 

.) 

我只通过讲解一个例子让大家初步了解一下. 

在上面提到的我的根目录的例子中,我们发现 System.map的连接数为2.它是普通文 

件.所以我想找到它的另一个连接. 

先要找到inode. 

'ls -ail'告诉我们: 

     2 drwxr-xr-x  14 root     root         1024 Sep 27 17:19 . 

     2 drwxr-xr-x  14 root     root         1024 Sep 27 17:19 .. 

    13 -rw-r--r--   2 root     root       219254 Sep 27 17:19 System.map 

  4019 drwxr-xr-x   2 root     root         2048 Sep 12 03:25 bin 

 56241 drwxr-xr-x   2 root     root         1024 Sep 27 17:20 boot 

    14 lrwxrwxrwx   1 root     root           13 Aug 31 06:40 var 

(等等) 

...这里第一个域的数字是inode---即这些连接所指向的文件系统数据结构.我们注意 

到根目录下'.'和'..'(当前目录及父目录)都指向同一个inode,这个inode是分配给 

根目录的.(任何其它目录并不是这样.) 

...所以我想找到这个文件系统中所有指向13号inode的连接. 

*(不能在其它被mount上的文件系统中找---它们每个都有自己的13号inode) 

下面的命令完成这项工作: 

find / -mount -inum 13 

...哇!这么简单."-mount"选项告诉find命令仅仅局限于本文件系统而不要越过任 

何mount点(它和-xdev选项一样). 

要想对某一目录下每个连接都使用这套步骤,其困难之处就在于如何找到每个文件 

所在的文件系统的根.在我的例子中做到这一点很简单,但靠不住,因为我想找的连 

接就在根目录里(而很明显,根目录就是其文件系统的根). 

如果我有一个脚本或程序能够"找到指定文件所在的文件系统的根"(叫它 

"fsrootof")---那么我就能写出脚本的其余部分: 

 find ... -type f -links +1 -printf "%i %p\n" | while read i f; do 

 find $(fsrootof $f) -mount -inum $i 

 done 

用这一段shell脚本,可以产生一个清单,列出连接数大于1的"普通文件"的inode和 

文件名/路径(这是由第一个'find'的-printf选项实现的).这个清单被输入一个简 

单的shell循环,以一个"inode"和一个"path"的形式读入每一行(随后分别以$i和 

$f引用它们).循环体调用我那个神秘的脚本或程序来"发现文件所在的文件系统的 

根"---然后把这些文件系统的根作为第二个find命令的搜索起始点. 

一时半会儿我还想不出用简单的shell脚本怎么实现"fsrootof".可能最好是用C或 

perl脚本(直接使用一些系统调用取得文件状态信息,并需要些技巧回溯到上级目录 

(沿着..连接)直到遇到mount点).我不得不研读find的源代码看看它到底是怎么实 

现的. 

所以,也许我应该把它留作"Linux公报读者挑战赛"(实现上述'fsrootof'). 

---------------------------------------------------------------------------

-----

版权所有 (C) 1998 NJLUG

出版于第35期《Linux公报》1998年12月 中文版第二期

---------------------------------------------------------------------------

-----

  

   rpm passwd links ipscript magickeys 

---------------------------------------------------------------------------

-----

    


--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: bin@mtlab.hit.edu.cn]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:2.645毫秒