UNIX 檔案系統基本操作
UNIX 檔案系統基本操作
為娛樂和利益而讀取目錄
簡介: 利用 readdir()
和 stat()
函式瀏覽目錄中的條目。因為 UNIX® 系統中存在大量的檔案和目錄,所以您需要了解如何使用 readdir()
函式處理這些目錄條目,以及如何使用 stat()
函式提取這些條目的相關資訊。在您的 UNIX 程式開發工作中,這些基礎的檔案系統操作可以很好地為您提供服務,讓您可以輕鬆地發現並讀取 UNIX 系統中檔案、目錄和符號連結。
UNIX® 中任何事物都是檔案 的觀點意味著,您將始終會與檔案和目錄打交道,無論您開發的是何種型別的應用程式。任何事物都儲存為檔案,從資料到配置檔案、甚至是裝置,在對 UNIX 程式設計經過幾個小時的學習之後,stdio.h 系統 Header 中的函式將能夠為您提供很好的幫助。
一個時常困擾 UNIX 程式設計新手的問題是,如何瀏覽一個目錄,並對其中的檔案、目錄和符號連結進行相應的處理。如何能夠獲取它們的列表,以及如何能夠確定它們究竟是什麼?
請繼續閱讀本文,以學習如何使用 dirent.h 函式系列 (opendir()/readdir()/closedir()
) 來讀取目錄中的條目,以及使用 stat()
函式來確定這些條目所對應的內容。
對於一個給定路徑的目錄,應該如何讀取其中的條目呢?您無法像操作檔案那樣開啟目錄(使用 open()
或 fopen()
函式),並且即便可以這樣做,所得到的資料可能是您正在使用的檔案系統的專用格式,而對於不十分熟悉的程式設計師來說,直接訪問這些資料將使情況變得更糟。
dirent.h 函式,opendir()
、readdir()
和 closedir()
,它們正是您所需要的。這些函式的使用與用來對檔案進行操作的 open/read/close 的習慣用法非常相似,但有一點除外:對於每個目錄條目,readdir()
函式一次返回一個指向特殊結構(struct dirent
型別)的指標。通常,對目錄進行瀏覽類似於清單 1 中所示的虛擬碼。
dir = opendir( "some/path/name" ) entry = readdir( dir ) while entry is not NULL: do_something_with( entry ) entry = readdir( dir ) closedir( dir ) |
在出現問題時,opendir()
和 readdir()
函式都會返回 NULL,並且將設定全域性變數 errno
的值,以指出所出現的錯誤。如果 readdir()
返回 NULL,並且 errno
為 0(有時也稱為 EOK 或 ENOERROR),則表示沒有其他的目錄條目。
有一點需要注意,每個目錄都包含“.”(對該目錄的引用)和“..”(對該目錄的父目錄的引用)條目。根據您所進行的操作,可能需要忽略對這些條目的處理。
請注意,readdir()
不是執行緒安全的,因為所返回的結構是儲存在函式庫中的一個靜態變數。大多數現代的 UNIX 系統都具有執行緒安全的 readdir_r()
,如果您正在編寫執行緒程式碼,可以使用這個函式作為替代。
POSIX 1003.1 標準僅僅為 struct dirent
定義了一個必需的條目,即 char
陣列 d_name
。這是用標準的以 NULL 結尾的字串表示的該條目的名稱。這個結構中任何其他內容都是特定於您的 UNIX 系統的。
的確如此,struct dirent
中其他所有內容 都是不可移植的。嚴格滿足一致性的系統不應該在其中包含任何其他的內容。如果您編寫了使用額外結構成員的程式碼,那麼您必須將其標記為不可移植的,並且包含一個完成相同任務的替換程式碼路徑,如果您認為這樣做特別友好的話。
例如,許多 UNIX 包含一個 d_type
成員和一些附加常量,這樣一來,您無需額外的 stat()
呼叫就可以檢查目錄條目的型別。除了減少另外的系統呼叫之外,這種不可移植的擴充套件還減少了從檔案系統獲取更多後設資料的開銷非常高的訪問操作。眾所周知,在大多數 UNIX 上,stat()
函式的執行速度非常慢。
除了獲取目錄中條目的名稱之外,您可能還需要一些附加資訊,以確定下一步要進行的操作。至少,僅根據目錄條目的名稱,您無法辨別檔案條目。
stat()
函式會將特定檔案的相關資訊填入 struct stat
結構中,如果您獲得的是檔案描述符而不是檔名,那麼作為替代,您可以使用 fstat()
函式。如果您想能夠檢測出符號連結,那麼可以對檔名使用 lstat()
。
與 readdir()
返回的 struct dirent
不同,struct stat
具有相當多的標準的、必需的成員:
st_mode
——檔案許可權(使用者、其他使用者、組)和標誌st_ino
——檔案序列號st_dev
——檔案裝置號st_nlink
——檔案連線計數st_uid
——所有者使用者 IDst_gid
——所有者組 IDst_size
——以位元組表示的檔案大小(針對普通檔案)st_atime
——最後的訪問時間st_mtime
——最後的修改時間st_ctime
——檔案的建立時間
對 st_mode
成員使用 S_*()
宏,這樣就可以找出您所處理的目錄條目的型別:
- S_ISBLK(mode)——是否為塊特殊檔案?(通常是某種基於塊的裝置)
- S_ISCHR(mode)——是否為字元特殊檔案?(通常是某種基於字元的裝置)
- S_ISDIR(mode)——是否為目錄?
- S_ISFIFO(mode)——是否為管道或 FIFO 特殊檔案?
- S_ISLNK(mode)——是否為符號連結?
- S_ISREG(mode)——是否為普通檔案?
眾所周知,在大多數檔案系統上,stat()
函式的執行速度非常慢,所以如果您打算在將來再次使用該資訊,可能需要對其進行快取。
通常,您並不關心符號連結。如果對符號連結呼叫 stat()
,那麼您將獲取該連結所指向的檔案的相關資訊。這和使用者的體驗是一致的,因為控制與該檔案互動的是目標檔案的許可權,而不是符號連結本身。
有些應用程式,如 ls
和備份程式,需要能夠顯示連結檔案本身的相關資訊,例如它所指向的檔案。當您使用 lstat()
來代替 stat()
時,以及當您出於特定的目的而需要獲取符號連結本身的相關資訊,而不是直接與其連結的檔案打交道時,情況也是這樣的。
既然已經學習瞭如何使用 readdir()
和 stat()
來查詢目錄中的條目,那麼讓我們來看看演示這些函式的一些實際程式碼。
這裡所介紹的程式碼將瀏覽命令列中指定的一個或多個目錄,並顯示在該目錄中找到的每個條目的相關資訊。當它找到另一個目錄時,它會對該目錄進行同樣的處理。對於符號連結,將顯示其目標檔案,並且還將顯示普通檔案的大小。將忽略特殊檔案。
如清單 2 所示,這個簡單的演示應用程式中包含了各種 Header 檔案。程式的開始塊中包含了大多數程式中使用的標準部分,並且後面的四項是在該程式中使用 readdir()
和 stat()
所必需的。
#include |
process_directory()
函式(開始於清單 3,結束於清單 6)讀取了指定的目錄,並顯示了每個條目的相關資訊。opendir()
返回的 DIR 指標與 fopen()
返回的 FILE 指標類似,它是一個用於跟蹤目錄流的作業系統特定的物件,您應該忽略其具體內容。
unsigned process_directory( char *theDir ) { DIR *dir = NULL; struct dirent entry; struct dirent *entryPtr = NULL; int retval = 0; unsigned count = 0; char pathName[PATH_MAX + 1]; /* Open the given directory, if you can. */ dir = opendir( theDir ); if( dir == NULL ) { printf( "Error opening %s: %s", theDir, strerror( errno ) ); return 0; } |
在開啟了指定的目錄之後,呼叫 readdir_r()
(請參見清單 4)以獲取關於第一個條目的資訊,隨後每次呼叫 readdir_r()
都將返回下一個條目,直到到達了目錄末尾,並且 entryPtr
被設定為 NULL。這裡還使用了 strncmp()
來檢查“.”和“..”條目,以便略過它們。如果不略過它們,您將永遠都在處理類似“theDir/./././././././././.”等這樣的目錄。
retval = readdir_r( dir, &entry, &entryPtr ); while( entryPtr != NULL ) { struct stat entryInfo; if( ( strncmp( entry.d_name, ".", PATH_MAX ) == 0 ) || ( strncmp( entry.d_name, "..", PATH_MAX ) == 0 ) ) { /* Short-circuit the . and .. entries. */ retval = readdir_r( dir, &entry, &entryPtr ); continue; } |
既然已經得到了目錄的條目名稱,那麼您需要構造一個更加完整的路徑(請參見清單 5),然後呼叫 lstat()
以獲取該條目的相關資訊。因為符號連結需要特殊的處理,所以這裡使用了 lstat()
函式。您可以使用 readlink()
函式找到其目標檔案。
如果該條目是一個目錄,那麼對這個目錄遞迴地呼叫 process_directory()
,並將其中所找到的條目數加到執行總數中。如果該條目是一個檔案,那麼顯示其名稱和位元組數(可在 struct stat
的 st_size
成員中找到)。
(void)strncpy( pathName, theDir, PATH_MAX ); (void)strncat( pathName, "/", PATH_MAX ); (void)strncat( pathName, entry.d_name, PATH_MAX ); if( lstat( pathName, &entryInfo ) == 0 ) { /* stat() succeeded, let's party */ count++; if( S_ISDIR( entryInfo.st_mode ) ) { /* directory */ printf( "processing %s/n", pathName ); count += process_directory( pathName ); } else if( S_ISREG( entryInfo.st_mode ) ) { /* regular file */ printf( "t%s has %lld bytesn", pathName, (long long)entryInfo.st_size ); } else if( S_ISLNK( entryInfo.st_mode ) ) { /* symbolic link */ char targetName[PATH_MAX + 1]; if( readlink( pathName, targetName, PATH_MAX ) != -1 ) { printf( "t%s -> %sn", pathName, targetName ); } else { printf( "t%s -> (invalid symbolic link!)n", pathName ); } } } else { printf( "Error statting %s: %sn", pathName, strerror( errno ) ); } |
在 while
迴圈的底部,讀取另一個目錄條目並對其進行處理。如果您完成了對目錄條目的處理,那麼關閉當前開啟的目錄,並返回經過處理的條目的數目。
retval = readdir_r( dir, &entry, &entryPtr ); } /* Close the directory and return the number of entries. */ (void)closedir( dir ); return count; } |
最後,清單 7 顯示了該程式的 main()
函式,它只是對命令列中傳遞的每個引數呼叫了 process_directory()
函式。一個真正的程式應該具有使用方法訊息,並且在使用者沒有指定任何引數時,提供某種形式的反饋資訊,但我把這項內容作為練習留給讀者。
/* readdir_demo main() * * Run through the specified directories, and pass them * to process_directory(). */ int main( int argc, char **argv ) { int idx = 0; unsigned count = 0; for( idx = 1; idx < argc; idx++ ) { count += process_directory( argv[idx] ); } return EXIT_SUCCESS; } |
這就是整個程式。儘管包含了較多的檔案,但處理目錄條目並不是十分困難。
使用 readdir()
和 stat()
函式瀏覽目錄中的條目並確定對其進行的額外處理,是非常簡單的,在您需要列舉目錄中的內容時,也可能會使用到這種處理方法。它是一種很實用的方法,但是對於一些沒有經驗的 UNIX 開發人員來說,卻難以掌握。本文的目的是降低其難度,使得 UNIX 開發人員能夠充分利用這些有價值的函式
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24790158/viewspace-1040153/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- unix檔案操作
- UNIX根檔案系統和附加檔案系統
- HP-UNIX 建立檔案系統
- linux/unix xfs大檔案系統Linux
- HP-UNIX下建立檔案系統
- UNIX系統操作命令(轉)
- 28 檔案基本操作
- 提升 UNIX 中檔案系統使用效率(轉)
- UNIX系統下各檔案的作用(轉)
- Unix檔案系統頁面監控實現
- 遷移和移動 UNIX 檔案系統(轉)
- Linux 檔案系統基本介紹Linux
- cephFS分散式檔案系統操作分散式
- 檔案系統操作與磁碟管理
- UNIX 系統操作入門(三)(轉)
- UNIX 系統操作入門(二)(轉)
- UNIX 系統操作入門(一)(轉)
- Unix檔案系統和pwd命令實現詳解
- HP UNIX--檔案系統和邏輯卷管理
- UNIX系統被刪檔案的恢復策略(轉)
- NFS-UNIX 中的網路檔案系統(轉)NFS
- iOS系統相簿基本操作iOS
- Linux/proc檔案系統操作指南Linux
- UNIX 系統中的文字操作簡介
- SCO UNIX--基本配置及操作(轉)
- 檔案的基本管理和XFS檔案系統備份恢復
- Linux系統的基本操作Linux
- UNIX作業系統概述及基本知識(轉)作業系統
- NodeJs fs(檔案系統簡單操作)NodeJS
- 在nodeJS中操作檔案系統(二)NodeJS
- Unix檔案系統頁面監控實現-效果頁面
- 轉載hp--unix下檔案系統的擴充套件套件
- 在linux及unix系統下批次刪除oracle檔案LinuxOracle
- I/O流以及檔案的基本操作
- Linux系統程式設計(2)——檔案與IO之系統呼叫與檔案IO操作Linux程式設計
- Linux 檔案系統相關的基本概念Linux
- 分散式檔案系統fastdfs_搭建和基本使用分散式AST
- Linux系統(一)檔案系統、壓縮、打包操作總結Linux