by falcon
2007-11-5
這一周我們來探討文件操作。在日常學習和工作中,我們總是在不斷的和各種文件打交道,這些文件包括普通的文本文件,可以執行的程序文件,帶有控制字符的文
檔、存放各種文件的目錄文件、網絡套接字文件、設備文件等。這些文件又具有諸如屬主、大小、創建和修改日期等各種屬性。文件對應文件系統的一些數據塊,對
應磁盤等存儲設備的一片連續空間,對應于顯示設備卻是一些具有不同形狀的字符集。
在這一節,為了把關注點定位在文件本身,我們不會深入探討文件系統以及存儲設備是如何組織文件的(在后續章節再深入探討,如果有興趣也可以提前看參考資料
[1][2][3][4][5]),而是探討我們對它最熟悉的一面,即把文件當成是一序列的字符(一個byte)集合看待。因此之前介紹的“shell編
程范例之字符串操作”在這里將會得到廣泛的應用,關于普通文件的讀寫操作我想我們已經用得非常熟練啦,那就是“重定向”,在這里,我們會把這部分獨立出來
介紹。關于文件在Linux下的“數字化”(文件描述符)高度抽象,“一切皆為文件”的哲學在shell編程里也得到了深刻的體現。
下面我們先來介紹文件的各種屬性,然后介紹普通文件的一般操作。
1、文件的各種屬性
首先,我們通過文件的結構體來看看文件到底有哪些屬性:
Code:
[Ctrl+A Select All]
下面逐次來了解這些屬性,如果需要查看某個文件的屬性,用stat命令就可以,會按照上面的結構體把信息列出來。另外,ls命令在跟上一定的參數后也可以顯示文件的相關屬性,比如-l參數。
1.1 文件類型
文件類型對應于上面的st_mode, 文件類型有很多,比如常規文件、符號鏈接(硬鏈接、軟鏈接)、管道文件、設備文件(符號設備、塊設備)、socket文件等,不同的文件類型對應不同的功能和作用。
1.1.1 在命令行簡單地區分各類文件:
Quote: |
$ ls -l total 12 drwxr-xr-x 2 root root 4096 2007-12-07 20:08 directory_file prw-r--r-- 1 root root 0 2007-12-07 20:18 fifo_pipe brw-r--r-- 1 root root 3, 1 2007-12-07 21:44 hda1_block_dev_file crw-r--r-- 1 root root 1, 3 2007-12-07 21:43 null_char_dev_file -rw-r--r-- 2 root root 506 2007-12-07 21:55 regular_file -rw-r--r-- 2 root root 506 2007-12-07 21:55 regular_file_hard_link lrwxrwxrwx 1 root root 12 2007-12-07 20:15 regular_file_soft_link -> regular_file $ stat directory_file/ File: `directory_file/' Size: 4096 Blocks: 8 IO Block: 4096 directory Device: 301h/769d Inode: 521521 Links: 2 Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2007-12-07 20:08:18.000000000 +0800 Modify: 2007-12-07 20:08:18.000000000 +0800 Change: 2007-12-07 20:08:18.000000000 +0800 $ stat null_char_dev_file File: `null_char_dev_file' Size: 0 Blocks: 0 IO Block: 4096 character special file Device: 301h/769d Inode: 521240 Links: 1 Device type: 1,3 Access: (0644/crw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2007-12-07 21:43:38.000000000 +0800 Modify: 2007-12-07 21:43:38.000000000 +0800 Change: 2007-12-07 21:43:38.000000000 +0800
|
說
明:
在ls命令結果每行的第一個字符我們可以看到,它們之間都不相同,這正好反應了不同文件的類型。d表示目錄,-表示普通文件(或者硬鏈接),l表示符號鏈
接,p表示管道文件,b和c分別表示塊設備和字符設備(另外s表示socket文件)。在stat命令的結果中,我們可以在第二行的最后找到說明,從上面
的操作可以看出,directory_file是目錄,stat命令的結果中用directory表示,而null_char_dev_file它則用
character special file說明。
1.1.2 簡單比較它們的異同
通常,我們只會用碰到目錄、普通文
件、以及符號鏈接,很少碰到其他類型的文件,不過這些文件還是各有用處的,如果要做嵌入式開發或者進程通信等,你可能會涉及到設備文件、有名管道
(FIFO)。下面我們通過簡單的操作來反應它們之間的區別(具體的原理可能會在下一節“shell編程范例之文件系統”介紹,如果感興趣,也可以提前到
網上找找設備文件的作用、快設備和字符設備的區別、以及驅動程序中如何編寫相關設備驅動等,可參考資料[12])。
Quote: |
// 對于普通文件:就是一序列字符的集合,所以可以讀、寫等 $ echo "hello, world" > regular_file $ cat regular_file hello, world // 目錄文件下,我們可以創建新的文件,所以目錄文件還有叫法:文件夾,到后面我們會分析目錄文件的結構體,它實際上存放了它下面的各個文件的文件名。 $ cd directory_file $ touch file1 file2 file3 // 對于有名管道,操作起來比較有意思:如果你要讀它,除非有內容,否則阻塞;如果你要寫它,除非有人來讀,否則阻塞。它常用于進程通信中。你可以打開兩個終端terminal1和terminal2,試試看: terminal1$ cat fifo_pipe #剛開始阻塞在這里,直到下面的寫動作發生,才打印test字符串 terminal2$ echo "test" > fifo_pipe // 關于塊設備,字符設備,上面的兩個設備文件對應于/dev/hda1和/dev/null,如果你用過u盤,或者是寫過簡單的腳本的話,這樣的用法你應該用過 :-) $ mount hda1_block_dev_file /mnt #掛載硬盤的第一個分區到/mnt下(關于掛載的原理,我們在下一節討論) $ echo "fewfewfef" > /dev/null #/dev/null像個黑洞,什么東西丟進去都消失殆盡 //
最后兩個文件分別是regular_file文件的硬鏈接和軟鏈接,你去讀寫它們,他們的內容是相同的,不過你去刪除它們,他們去互不相干,硬鏈接和軟鏈
接又有什么不同呢?前者可以說就是原文件,后者呢只是有那么一個inode,但沒有實際的存儲空間,建議用stat命令查看它們之間的區別,包括它們的
Blocks,inode等值,也可以考慮用diff比較它們的大小。 $ ls regular_file* ls regular_file* -l -rw-r--r-- 2 root root 204800 2007-12-07 22:30 regular_file -rw-r--r-- 2 root root 204800 2007-12-07 22:30 regular_file_hard_link lrwxrwxrwx 1 root root 12 2007-12-07 20:15 regular_file_soft_link -> regular_file $ rm regular_file #刪除原文件 $ cat regular_file_hard_link #硬鏈接還在,而且里頭的內容還有呢 fefe $ cat regular_file_soft_link cat: regular_file_soft_link: No such file or directory #雖然軟鏈接文件本身還在,不過因為它本身不存儲內容,所以讀不大東西拉,這就是軟鏈接和硬鏈接的區別,該知道則么用它們了吧。
|
1.1.3 普通文件再分類
文
件類型從Linux文件系統那么一個級別分了以上那么多類型,不過普通文件還是可以再分的(根據文件內容的”數據結構“分),比如常見的文本文件,可執行
的ELF文件,odt文檔,jpg圖片格式,swap分區文件,pdf文件。除了文件文件外,它們大多是二進制文件,有特定的結構,因此需要有專門的工具
來創建和編輯它們。關于各類文件的格式,可以參考相關文檔標準。不過如果能夠了解Linux下可執行的ELF文件的工作原理,可能對你非常有用。所以,如
果有興趣,建議閱讀一下參考資料中和ELF文件相關部分。
雖然各類普通文件都有專屬的操作工具,但是我們還是可以直接讀、寫它們。具體的操作我們在“文件的基本操作部分深入介紹”,這里先提到這么幾個工具。
od:以八進制或者其他格式“導出”文件內容。
strings:讀出文件中的字符(可打印的字符)
gcc,gdb,readelf,objdump等:ELF文件分析、處理工具(gcc編譯器、gdb調試器、readelf分析elf文件,objdump反編譯工具)
這
里補充一個非常重要的命令,file,這個命令用來查看各類文件的屬性。和stat命令相比,它可以進一步識別普通文件,即stat命令顯示的
regular file。因為regular
file可以有各種不同的結構,因此在操作系統的支持下得到不同的解釋,執行不同的動作。雖然,Linux下,文件也會加上特定的后綴以便用戶能夠方便地
識別文件的類型,但是Linux操作系統根據文件頭識別各類文件,而不是文件后綴(請參考資料[23]),這樣,在解釋相應的文件時就更不容易出錯。下面
我們簡單介紹file命令的用法。
Quote: |
$ file ./ ./: directory $ file /etc/profile /etc/profile: ASCII English text $ file /lib/libc-2.5.so /lib/libc-2.5.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped $ file /bin/test /bin/test: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped $ file /dev/hda /dev/hda: block special (3/0) $ file /dev/console /dev/console: character special (5/1) $ cp /etc/profile . $ tar zcf profile.tar.gz profile $ file profile.tar.gz profile.tar.gz: gzip compressed data, from Unix, last modified: Tue Jan 4 18:53:53 2000 $ mkfifo fifo_test $ file fifo_test fifo_test: fifo (named pipe)
|
更多用法見file命令的手冊,關于file命令的實現原理,請參考magic的手冊(看看/etc/file/magic文件,了解什么是文件的magic number等)。
1.2 文件屬主
Linux
作為一個多用戶系統,為多用戶使用同一個系統提供了極大的方便,比如對于系統上的文件,它通過屬主來區分不同的用戶,以便分配它們對不同文件的操作權限。
為了更方便地管理,文件屬主包括該文件所屬用戶,以及該文件所屬的用戶組,因為用戶可以屬于多個組。我們先來簡單介紹Linux下用戶和組的管理(更多信
息請參考資料[15])。
Linux下提供了一組命令用于管理用戶和組,比如用戶創建它們的useradd和groupadd,用戶刪除
它們的userdel和groupdel,另外,passwd命令用于修改用戶密碼。當然,Linux還提供了兩個相應的配置,即/etc/passwd
和/etc/group,另外,有些系統還把密碼單獨放到了配置文件/etc/shadow中。關于它們的詳細用法請參考資料[15],在這里我們不介紹
了,僅介紹文件和用戶之間的一些關系。
比如,修改文件的屬主:
Quote: |
$ chown 用戶名:組名 文件名
|
如果要遞歸的修改某個目錄下所有用戶屬主,可以添加-R選項。
在本節開頭我們列的文件結構體中,可以看到僅僅有用戶ID和組ID的信息,但ls -l的結果卻顯示了用戶名和組名信息,這個是怎么實現的呢?下面先看看-n的結果:
Quote: |
$ ls -n regular_file -rw-r--r-- 1 0 0 115 2007-12-07 23:45 regular_file $ ls -l regular_file -rw-r--r-- 1 root root 115 2007-12-07 23:45 regular_file
|
可
以看到,ls -n顯示了用戶ID和組ID,而ls
-l顯示了它們的名字。還記得上面提到的兩個配置文件/etc/passwd和/etc/group文件么?它們分別存放了用戶ID和用戶名,組ID和組
名的對應關系,因此很容易想到ls -l命令在實現時是如何通過文件結構體的ID信息找到它們對應的名字信息的。如果想對ls
-l命令的實現有更進一步的了解,可以用strace跟蹤看看它是否讀取了/etc/passwd和/etc/group這個兩個文件。
Quote: |
$ strace -f -o strace.log ls -l regular_file $ cat strace.log | egrep "passwd|group|shadow" 2989 open("/etc/passwd", O_RDONLY) = 3 2989 open("/etc/group", O_RDONLY) = 3
|
說明:strace是一個非常有用的工具,可以用來跟蹤系統調用和信號。如同gdb等其他強大的工具一樣,它基于系統的ptrace系統調用實現,所以如果你感興趣,可以好好研究一下這個工具。
實際上,把屬主和權限分開介紹實際上不太好,因為只有它們兩者結合才使得多用戶系統成為可能,否則無法隔離不同用戶對某個文件的操作,所以下面來介紹文件操作權限。
1.3 不同屬主的文件操作權限
從ls
-l命令的結果的第一列的后9個字符中,我們可以看到類似這樣的信息rwxr-xr-x,它們對應于文件結構體的st_mode部分(st_mode包含
文件類型信息和文件權限信息兩部分)。這類信息可以分成三部分,即rwx,r-x,r-x,分別對應該文件所屬用戶,所屬組,其他組對該文件的操作權限,
如果有rwx中任何一個表示可讀、可寫、可執行,如果為-表示沒有這個權限。對應的,可以用八進制來表示它,比如rwxr-xr-x就可表示成二進制
111101101,對應的八進制則為755。正是因為這樣,我們要修改文件的操作權限時,也可以有多種方式來實現,它們都可通過chmod命令來修改。
比如,把regular_file的文件權限修改為所有用戶都可讀、可寫、可執行,即rwxrwxrwx,也可表示為111111111,翻譯成八進制,則為777。這樣就可以通過兩中方式修改這個權限。
Quote: |
$ chmod a+rwx regular_file 或 $ chmod 777 regular_file
|
說明:a指所用用戶,如果只想給用戶本身可讀可寫可執行權限,那么可以把a換成u;而+就是添加權限,相反的,如果想去掉某個權限,用-,而rwx則對應可讀、可寫、可執行。更多用法見chmod命令的幫助。
實際上除了這些權限外,還有兩個涉及到安全方面的權限,即setuid/setgid和只讀控制等。
如果設置了文件(程序或者命令)的setuid/setgid權限,那么用戶將可以root身份去執行該文件,因此,這將可能帶來安全隱患;如果設置了文件的只讀權限,那么用戶將僅僅對該文件將僅有可讀權限,這為避免諸如rm -rf的“可惡”操作帶來一定的庇佑。
setuid/setgid示例:通過普通用戶執行passwd命令。默認情況下,系統是不允許普通用戶執行它的。
Quote: |
$ ls -l /usr/bin/passwd -rwx--x--x 1 root root 36092 2007-06-19 14:59 /usr/bin/passwd $ su #切換到root用戶,給程序或者命令添加“粘著位” $ chmod +s /usr/bin/passwd $ ls -l /usr/bin/passwd -rws--s--x 1 root root 36092 2007-06-19 14:59 /usr/bin/passwd $ exit $ passwd #普通用戶通過執行該命令,修改自己的密碼
|
說
明:“setuid
和setgid位是讓普通用戶可以以root用戶的角色運行只有root帳號才能運行的程序或命令。”雖然這在一定程度上為管理提供了方便,比如上面的操
作讓普通用戶可以修改自己的帳號,而不是要root帳號去為每個用戶做這些工作。關于setuid/setgid的更多詳細解釋,請參考資料[16]。
只讀權限示例:給重要文件加鎖(添加不可修改位[immutable])),以避免各種誤操作帶來的災難性后果(例如: rm -rf)
Quote: |
$ chattr +i regular_file $ lsattr regular_file ----i-------- regular_file $ rm regular_file #加了immutable位以后,你無法對文件進行任何“破壞性”的活動啦 rm: remove write-protected regular file `regular_file'? y rm: cannot remove `regular_file': Operation not permitted $ chattr -i regular_file #如果想對它進行常規操作,那么可以把這個位去掉 $ rm regular_file
|
說明:chattr可以用設置文件的特殊權限,更多用法請參考chattr的幫助。
1.4 文件大小
普
通文件是文件內容的大小,而目錄作為一個特殊的文件,它存放的內容是以目錄結構體組織的各類文件信息,所以目錄的大小一般都是固定的,它存放的文件個數自
然也就有上限,即少于它的大小除以文件名的長度。設備文件的文件大小則對應設備的主、次設備號,而有名管道文件因為特殊的讀寫性質,所以大小常是0。硬鏈
接(目錄文件不能創建硬鏈接)實質上是原文件的一個完整的拷比,因此,它的大小就是原文件的大小。而軟鏈接只是一個inode,存放了一個指向原文件的指
針,因此它的大小僅僅是原文件名的字節數。下面我們通過演示增加記憶。
原文件,鏈接文件文件大小的示例:
Quote: |
$ echo -n "abcde" > regular_file #往regular_file寫入5字節 $ ls -l regular_file* -rw-r--r-- 2 root root 5 2007-12-08 15:28 regular_file -rw-r--r-- 2 root root 5 2007-12-08 15:28 regular_file_hard_file lrwxrwxrwx 1 root root 12 2007-12-07 20:15 regular_file_soft_link -> regular_file lrwxrwxrwx 1 root root 22 2007-12-08 15:21 regular_file_soft_link_link -> regular_file_soft_link $ i="regular_file" $ j="regular_file_soft_link" $ echo ${#i} ${#j} #可以參考,軟鏈接存放的剛好是它們指向的原文件的文件名的字節數 12 22
|
設備號對應的文件大小:主、次設備號
Quote: |
$ ls -l hda1_block_dev_file brw-r--r-- 1 root root 3, 1 2007-12-07 21:44 hda1_block_dev_file $ ls -l null_char_dev_file crw-r--r-- 1 root root 1, 3 2007-12-07 21:43 null_char_dev_file
|
補
充:主(major)、次(minor)設備號的作用有不同。當一個設備文件被打開時,內核會根據主設備號(major
number)去查找在內核中已經以主設備號注冊的驅動(可以cat
/proc/devices查看已經注冊的驅動號和主設備號的對應情況),而次設備號(minor
number)則是通過內核傳遞給了驅動本身(參考“The Linux
Primer第十章”)。因此,對于內核而言,通過主設備號就可以找到對應的驅動去識別某個設備,而對于驅動而言,為了能夠更復雜地訪問設備,比如訪問設
備的不同部分(如硬件通過分區分成不同部分,而出現hda1,hda2,hda3等),比如產生不同要求的隨機數(如/dev/random和
/dev/urandom等)。
目錄文件的大小,為什么是這樣呢?看看下面的目錄結構體的大小,目錄文件的Block中存放的該目錄下所有文件名的入口(文件名)。
Quote: |
$ ls -ld directory_file/ drwxr-xr-x 2 root root 4096 2007-12-07 23:14 directory_file/
|
目錄的結構體如下:
Code:
[Ctrl+A Select All]
1.5 文件訪問、更新、修改時間
文件的時間屬性可以記錄用戶對文件的操作信息,在系統管理、判斷文件版本信息等情況下將為管理員提供參考。因此,在閱讀文件時,建議用cat等閱讀工具,不要用編輯工具vim去閱讀,因為即使你沒有做任何修改操作,一旦你執行了保存命令,你將修改文件的時間戳信息。
1.6 文件名
文件名并沒有存放在文件結構體里,而是存放在它所在的目錄結構體中。所以,在目錄的同一級別中,文件名必須是唯一的。
2、文件的基本操作
對于文件,常見的操作包括創建、刪除、修改、讀、寫等。
[關于各種操作對應的“背后動作”我們將在下一節(shell編程范例之文件系統操作)詳細分析]
2.1 創建以上各類文件
socket文件是一類特殊的文件,可以通過C語言創建,這里不做介紹(暫時不知道是否可以用命令直接創建),其他文件我們將通過命令創建。
Quote: |
$ touch regular_file #創建普通文件 $ mkdir directory_file #創建目錄文件,目錄文件里頭可以包含更多文件 $ ln regular_file regular_file_hard_link #硬鏈接,是原文件的一個完整拷比 $ ln -s regular_file regular_file_soft_link #類似一個文件指針,指向原文件 $ mkfifo fifo_pipe #或者通過 "mknod fifo_pipe p" 來創建,FIFO滿足先進先出的特點 $ mknod hda1_block_dev_file b 3 1 #塊設備 $ mknod null_char_dev_file c 1 3 #字符設備
|
創
建一個文件實際上是在文件系統中添加了一個節點(inode),該節點信息將保存到文件系統的節點表中。更形象地說,就是在一顆樹上長了一顆新的葉子(文
件)或者枝條(目錄文件,上面還可以長葉子的那種),這些可以通過tree命令或者ls命令形象地呈現出來。文件系統從日常使用的角度,完全可以當成一顆
倒立的樹來看,因為它們太像了,太容易記憶啦(關于文件系統中一些常見的節點請參考資料[17])。
Quote: |
$ tree 當前目錄 或者 $ ls 當前目錄
|
2.2 刪除
刪
除文件最直接的印象是這個文件再也不存在啦,這同樣可以通過ls或者tree命令呈現出來,就像樹木被砍掉一個分支或者摘掉一片葉子一樣。實際上,這些文
件刪除之后,并不是立即消失了,而是僅僅做了刪除標記,因此,如果刪除之后,沒有相關的磁盤寫操作把相應的磁盤空間“覆蓋”,那么原理上是可以恢復的(雖
然如此,但是這樣的工作往往是很麻煩的,所以在刪除一些重要數據時,請務必三思而后行,比如做好備份工作),相應的做法可以參考資料[18]到[21]。
具體刪除文件的命令有rm,如果要刪除空目錄,可以用rmdir命令。例如:
Quote: |
$ rm regular_file $ rmdir directory_file $ rm -r directory_file_not_empty
|
rm
有兩個非常重要的參數,一個是-f,這個命令是非常“野蠻的”(關于rm -rf的那些鐘愛者輕強烈建議您閱讀<unix
haters>的相應章節),它估計給很多linux
user帶來了痛苦,另外一個是-i,這個命令是非常“溫柔的”,它估計讓很多用戶感覺煩躁不已過。用哪個還是根據您的“心情”吧,如果做好了充分的備份
工作,或者采取了一些有效避免災難性后果的動作的話,您在做這些工作的時候就可以放心一些啦。
2.3 復制
文件的復制通常是指文件內容的“臨時”復制。通過這一節開頭的介紹,我們應該了解到,文件的硬鏈接和軟鏈接在某種意義上說也是“文件的復制”,前者同步復制文件內容,后者在讀寫的情況下同步“復制”文件內容。例如:
Quote: |
// 用cp命令常規地復制文件(復制目錄需要-r選項) $ cp regular_file regular_file_copy $ cp -r diretory_file directory_file_copy // 創建硬鏈接(link和copy不同之處是后者是同步更新,前者則不然,復制之后兩者不再相關) $ ln regular_file regular_file_hard_link // 創建軟鏈接 $ ln -s regular_file regluar_file_soft_link
|
2.4 修改文件名
修改文件名實際上僅僅修改了文件名標識符。我們可以通過mv命令來實現修改文件名操作(即重命名)。
Quote: |
$ mv regular_file regular_file_new_name
|
2.5 編輯文件
編
輯文件實際上是操作文件的內容,對應普通文本文件的編輯,這里主要涉及到文件內容的讀、寫、追加、刪除等。這些工作通常會通過專門的編輯器來做,這類編輯
器有命令行下的vim、emacs和圖形界面下的gedit,kedit等。如果是一些特定的文件,會有專門的編輯和處理工具,比如圖像處理軟件
gimp,文檔編輯軟件OpenOffice等。這些工具一般都會有專門的教程。關于vim的用法,建議閱讀這篇帖子:VIM高級命令集錦
下面主要簡單介紹Linux下通過重定向來實現文件的這些常規的編輯操作(更多有意思的用法見參考資料[6])。
Quote: |
// 創建一個文件并寫入abcde $ echo "abcde" > new_regular_file // 再往上面的文件中追加一行abcde $ echo "abcde" >> new_regular_file // 按行讀一個文件 $ while read LINE; do echo $LINE; done < test.sh
|
提示:如果要把包含重定向的字符串變量當作命令來執行,請使用eval命令,否則無法解釋重定向。例如,
Quote: |
$ redirect="echo \"abcde\" >test_redirect_file" $ $redirect #這里會把>當作字符 > 打印出來,而不會當作 重定向 解釋 "abcde" >test_redirect_file $ eval $redirect #這樣才會把 > 解釋成 重定向 $ cat test_redirect_file abcde
|
2.6 壓縮/解壓縮文件
壓縮和解壓縮文件在一定意義上來說是為了方便文件內容的傳輸,不過也可能有一些特定的用途,比如內核和文件系統的映像文件等(更多相關的知識請參考資料[22])。
這里僅介紹幾種常見的壓縮和解壓縮方法:
Quote: |
// tar, 這在某種意義上來不是文件的壓縮,僅僅是“把一堆東西放在一起,當成一個文件來看”。 $ tar -cf file.tar file #壓縮 $ tar -xf file.tar #解壓 // gz $ gzip -9 file $ gunzip file // tar.gz $ tar -zcf file.tar.gz file $ tar -zxf file.tar.gz // bz2 $ bzip2 file $ bunzip2 file // tar.bz2 $ tar -jcf file.tar.bz2 file $ tar -jxf file.tar.bz2
|
通過上面的演示,我們應該已經非常清楚tar命令,bzip2/bunzip2,gzip/gunzip命令的角色了吧?如果還不清楚,多操作和比較一些上面的命令,并查看它們的手冊:man tar...
2.7 文件搜索(文件定位)
文
件搜索是指在某個目錄層次中找出具有某些屬性的文件在文件系統中的位置,這個位置如果擴展到整個網絡,那么可以表示為一個URL地址,對于本地的地址,可
以表示為file://+本地路徑。本地路徑在Linux系統下是以/開頭,例如,每個用戶的家目錄可以表示為:file:///home/。下面僅僅介
紹本地文件搜索的一些辦法。
find命令提供了一種“及時的”搜索辦法,它根據用戶的請求,在指定的目錄層次中遍歷所有文件直到找到需要
的文件為止。而updatedb+locate提供了一種“快速的”的搜索策略,updatedb更新并產生一個本地文件數據庫,而locate通過文件
名檢索這個數據庫以便快速找到相應的文件。前者支持通過各種文件屬性進行搜索,并且提供了一個接口(-exec選項)用于處理搜索后的文件。因此為“單條
命令”腳本的愛好者提供了極大的方便,不過對于根據文件名的搜索而言,updatedb+locate的方式在搜索效率上會有明顯提高。下面簡單介紹這兩
種方法:
Quote: |
// find命令基本使用演示 $ find ./ -name "*.c" -o -name "*.h" #找出所有的C語言文件,-o是或者 $ find ./ \( -name "*.c" -o -name "*.h" \) -exec mv '{}' ./c_files/ \; #把找到的文件移到c_files下,這種用法非常有趣,不過她可以用xargs命令替代,例如 $ find ./ -name "*.c" -o -name "*.h" | xargs -i mv '{}' ./c_files/ #如果要對文件做更復雜的操作,可以考慮把mv改寫為你自己的處理命令,例如,我需要修改所有的文件名后綴為大寫。 $ find ./ -name "*.c" -o -name "*.h" | xargs -i ./toupper.sh '{}' ./c_files/ #toupper.sh就是我們需要實現的轉換小寫為大寫的一個處理文件,具體實現如下: $ cat toupper.sh #!/bin/bash
# the {} will be expended to the current line and becomen the first argument of this script FROM=$1 BASENAME=${FROM##*/}
BASE=${BASENAME%.*} SUFFIX=${BASENAME##*.}
TOSUFFIX="$(echo $SUFFIX | tr '[a-z]' '[A-Z]')" TO=$2/$BASE.$TOSUFFIX COM="mv $FROM $TO" echo $COM eval $COM // updatedb+locate基本使用演示 $ updatedb #更新庫 $ locate find*.gz #查找包含find字符串的所有gz壓縮包
|
實際上,除了上面兩種命令外,Linux下還有命令查找工具:which和whereis,前者用于返回某個命令的全路徑,而后者用于返回某個命令、源文件、man文件的路徑。例如,我們需要查找find命令的絕對路徑:
Quote: |
$ which find /usr/bin/find $ whereis find find:
/usr/bin/find /usr/X11R6/bin/find /usr/bin/X11/find /usr/X11/bin/find
/usr/man/man1/find.1.gz /usr/share/man/man1/find.1.gz
/usr/X11/man/man1/find.1.gz
|
需
要提到的是,如果想根據文件的內容搜索文件,那么find和updatedb+locate以及which,whereis都無能為例啦,可選的方法是
grep,sed等命令,前者在加上-r參數以后可以在指定目錄下文件中搜索指定的文件內容,后者再使用-i參數后,可以對文件內容進行替換。它們的基本
用法在前面的章節中我們已經詳細介紹了,所以這里就不敘述。
值得強調的是,這些命令對文件的操作是非常有意義的。它們在某個程度上把文件
系統結構給抽象了,使得對整個文件系統的操作簡化為對單個文件的操作,而單個文件如果僅僅考慮文本部分,那么最終卻轉化成了我們之前的字符串操作,即我們
上一節討論過的內容。為了更清楚的了解文件的組織結構,文件之間的關系,在下一節我們將深入探討文件系統。
參考資料
[1] Linux 系統的基本組成和文件系統結構
http://forum.ubuntu.org.cn/weblog_entry.php?e=332&sid=3ceee92718a77d5eef867497470ecc7b
[2] 從文件 I/O 看 Linux 的虛擬文件系統
http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/
[3] Linux 文件系統剖析
http://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/index.html?ca=drs-cn
[4] 《Linux 核心》第九章 文件系統
http://man.chinaunix.net/tech/lyceum/linuxK/fs/filesystem.html
[5] Linux Device Drivers, 3rd Edition
http://linuxdriver.co.il/ldd3
[6] 技巧:Linux I/O重定向的一些小技巧
http://www.ibm.com/developerworks/cn/linux/l-iotips/index.html
[7] Intel平臺下Linux中ELF文件動態鏈接的加載、解析及實例分析
http://www.ibm.com/developerworks/cn/linux/l-elf/part1/index.html
http://www.ibm.com/developerworks/cn/linux/l-elf/part2/index.html
[8] Shell腳本調試技術
http://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/index.html
[9] ELF文件格式及程序加載執行過程總匯
http://linuxsir.org/bbs/showthread.php?p=1196195
[10] Linux下C語言編程——文件的操作
http://fanqiang.chinaunix.net/a4/b2/20010508/113315.html
[11] 創建設備文件簡單例程
http://www.erya.cn/archives/200604/38.html
[12] Linux Device Driver, 3rd Version
http://linuxdriver.co.il/ldd3/
[13] "Linux下C語言編程" 的 文件操作 部分
http://www.mwjx.com/aboutfish/private/book/linux_c.txt
[14] Linux的inode是什么?
http://bbs.51cto.com/archiver/next-415377.html
[15] Linux 用戶管理工具介紹
http://fedora.linuxsir.org/main/?q=node/106
[16] Linux 文件和目錄的屬性
http://www.linuxsir.org/main/?q=node/196#5.1
[17] Filesystem Hierarchy Standard
http://www.pathname.com/fhs/pub/fhs-2.3.html#INTRODUCTION
[18] 學會恢復 Linux系統里被刪除的 Ext3文件
http://tech.ccidnet.com/art/237/20070720/1150559_1.html
[19] reiserfs文件系統反刪除(Undelete)操作的實踐
http://www.linuxsir.org/main/?q=node/118#2
[20] 使用mc恢復被刪除文件
http://bbs.tech.ccidnet.com/read.php?tid=48372
[21] Linux ext3 刪除文件原理
http://www.pet100.cn/blog/html/57/1057-1235.html
[22] Linux壓縮/解壓縮方式大全
http://www.chinaitlab.com/www/techspecial/tar/
[23] Everything is a byte

http://www.reteam.org/papers/e56.pdf
后記:
[1]
考慮到文件和文件系統的重要性,我們將把它分成三個小節來介紹:文件、文件系統、程序與進程。在文件這一部分,我們主要介紹文件的基本屬性和常規操作,在
文件系統那部分,將深入探討Linux 文件系統的各個部分(包括Linux
文件系統的結構、具體某個文件系統的大體結構分析、底層驅動的工作原理),在程序與進程一節將專門討論可執行文件的相關內容(包括不同的程序類型、加載執
行過程、不同進程之間的交互[命令管道和無名管道、信號通信]、對進程的控制等)。
[2] 有必要討論清楚 目錄大小 的含義,另外,最好把一些常規的文件操作全部考慮到,包括文件的讀、寫、執行、刪除、修改、復制、壓縮/解壓縮等。
[3] 下午剛從上海回來,比賽結果很“糟糕”,不過到現在已經不重要了,關鍵是通過決賽發現了很多不足,發現了設計在系統開發中的關鍵角色,并且發現了上海是個美麗的城市,上交也是個美麗的大學。回來就開始整理這個因為比賽落下了兩周的blog。
[4] 12月15日,添加文件搜索部分內容。
關于文件系統、文件相關的章節,建議參考《Unix環境高級編程》第3-6章。
補充一個有趣的例子,來自《unix環境高級編程》P75
Quote: |
//創建一個符號鏈接,雖然前面的文件不存在,但是符號鏈接還是創建了 $ ln -s /path/to/a/file/notexist notexist //不過,試圖訪問這個符號鏈接時卻試圖訪問前面那個鏈接,所以找不到那個文件,但提示的時候卻提示符號鏈接名指定的文件不存在,所以要千萬注意這個有趣的情況 $ cat notexist cat: notexist: No such file or directory
|
另外一個例子是,即使之前那個符號鏈接指向一個正確的文件(這里以目錄為例),但是那個文件可能會被刪除。
Quote: |
$ mkdir -p /tmp/path/to/a/file/notexist $ cat notexist1/ cat: notexist1/: Is a directory $ rmdir /tmp/path/to/a/file/notexist $ cat notexist1 cat: notexist1: No such file or directory
|
所以,在試圖訪問一個符號鏈接的時候,如果提示:"文件名: 沒有這個文件或目錄"的話,可能是該符號鏈接指向的文件或者目錄已經被刪除了,或者在創建該符號鏈接的時候它們就根本不存在。
在上面的操作中,我們發現符號鏈接文件的長度剛好是該符號鏈接指向的文件或者目錄名字字符串的長度,原因是符號鏈接文件存放的內容剛好是它們指向的文件或目錄名。
符號鏈接還會有一個有趣的問題:雖然我們可以創建一個目錄的符號鏈接,但是當你試圖從該符號鏈接返回到原有目錄的上級時是不可能的(cd
..),因為它的父目錄是符號鏈接文件本身所在的父目錄,但是通過getcwd函數獲取的卻是該符號鏈接指向的目錄的全路徑,而并不是它實際所在目錄加上
它本身。因此,如果不注意這個細節,容易在編寫shell腳本時出問題。
由于內核一般都設有內核緩沖區,所以在寫一個文件時可能并沒有立即寫入磁盤等存儲設備,需要特別注意這個問題。不過可以使用sync命令調用sync函數來強制理解寫入磁盤,不過這個貌似也會有30s左右的延遲,因為它通過一個sync精靈進程來檢查。
--摘自《unix環境高級編程》P89
演示,
做嵌入式開發時,把文件系統燒寫到開發板以后最后是執行一下sync,確保確實寫入了。
Quote: |
$ lilo -C /path/to/lilo.conf $ sync
|