問題介紹
最近使用Linux C進行編程的時候,當遞歸讀取目錄的時候會發現stat函數一直再報錯(No such file or directory),經過一晚上的修改bug,發現我的代碼中的stat函數只能訪問用戶當前所在的路徑下的文件(即’pwd‘命令所提示的目錄)。
例如:此時我所在的路徑為:/home/mrzhi/Desktop/Linux-網絡編程/Unix_systems_programming/Chapter5_File_system
結果:可以發現該代碼可以正確訪問所在路徑下的文件,但是該路徑下的文件夾卻無法遞歸訪問。但是該代碼中的opendir和reddir函數可以正確打開和讀取該路徑下的文件夾。但是卻無法使用stat函數返回文件的屬性。
問題分析
這里我們先查看一下stat函數的定義
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
/* These functions return information about a file, in the buffer pointed to by statbuf.
No permissions are required on the file itself, **but—in the case of stat(), fstatat(),
and lstat()—execute (search) permission is required on all of the directories in pathname that lead to the file.**
* stat() and fstatat() retrieve information about the file pointed to by pathname;
the differences for fstatat() are described below.
*/
可以發現其中有這樣一句話:but——in the case of stat, fstatat(), and lstat() - execute (search) permission is required on all of the directories in pathname that lead to the file。
這句話的意思是路徑名中指向該文件的所有目錄都必須具有執行(搜索權限)。也就是我所在目錄下的test必須具備執行或搜索權限。
錯誤原因:
只有文件為絕對路徑的情況下,才可以獲取文件的stat狀態。而剛開始的代碼中直接為./test/bbb.txt,此時pathname會直接訪問根目錄下的test/bbb.txt,但是我的根目錄下沒有這個文件,因此會報“No Such file or directory”的錯誤。要切記使用絕對路徑。
代碼示例
我的代碼示例如下:
**// 這個一個使用opendir,readdir和closedir等對目錄操作的函數
// 該程序顯示的是目錄中包含的文件名,其實這個目錄的路徑名被作為命令行參數傳送
// note:ls命令會按照字母順序對文件名進行排序,而readdir函數則按照起在目錄文件中出現的順序顯示文件名
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <limits.h>
#include <sys/stat.h>
int printDir (char *path) {
DIR *dr;
int ret;
struct dirent *direntp;
struct stat *statbuf; // struct stat 用來保存stat和lstat函數中返回的信息
// note: 如果statbuf使用指針,則需要使用malloc函數為其分配內存空間,不如直接使用statbuf(不加指針)方便
//printf("sizeof(struct stat) = %d\n", sizeof(struct stat));
// 使用opendir函數打開所指定的目錄
dr = opendir(path);
if(dr == NULL) {
perror("Failed to open directory");
return -1;
}
// 打印目錄中的文件
statbuf = (struct stat*)malloc(sizeof(struct stat));
while ( (direntp = readdir(dr)) != NULL) {
// fprintf(stderr, "打印文件: ");
// 調用stat函數查看文件的狀態
fprintf(stderr, "%s", direntp->d_name);
/* 在該函數中,stat只能訪問用戶所在的當前目錄-->當前的原因我也不清楚*/
ret = stat(direntp->d_name, statbuf);
if (ret == -1) {
//fprintf(stderr, "errno is %d\n", errno);
perror(" : Failed to get file status");
//return -1;
} else {
// 其中st_atim表示最后一次訪問的時間,st_time表示最后一次數據修改的時間
// st_ctime表示最后一次文件狀態改變的時間
// ctime返回的字符串會以換行符結束,因此可以不用\n
printf(" : last accessed at %s", ctime(&(statbuf->st_atim)) );
// 如果該文件是個目錄,則遞歸打印目錄中的文件
if (*direntp->d_name != '.' && S_ISDIR(statbuf->st_mode)) {
printf("***************************%s is directory!***********\n", direntp->d_name);
printDir(direntp->d_name);
printf("***************************%s ********************\n", direntp->d_name);
}
}
}
while (direntp == NULL && errno == EINTR);
// note:使用完畢后一定要關閉目錄
closedir(dr);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s dir\n", argv[1]);
return -1;
}
//printf("%s\n", argv[1]);
int ret = printDir(argv[1]);
return 0;
}**
修改后的代碼:
只有文件為絕對路徑的情況下,才可以獲取文件的stat狀態。因此這里對printDir函數中的while部分代碼進行修改即可。
// 打印目錄中的文件
while ( (direntp = readdir(dr)) != NULL) {
// fprintf(stderr, "打印文件: ");
fprintf(stderr, "%s", direntp->d_name);
char buf[1024] = {0};
sprintf(buf, "%s/%s", path, direntp->d_name);
// 調用stat函數查看文件的狀態
/* 在該函數中,stat只能訪問用戶所在的當前目錄-->是路徑的問題, 需要絕對路徑才可以訪問*/
ret = stat(buf, &statbuf);
if (ret == -1) {
perror(" : Failed to get file status");
//return -1;
} else {
// 其中st_atim表示最后一次訪問的時間,st_time表示最后一次數據修改的時間
// st_ctime表示最后一次文件狀態改變的時間
// ctime返回的字符串會以換行符結束,因此可以不用\n
printf(" : last accessed at %s", ctime(&statbuf.st_atim) );
// 如果該文件是個目錄,則遞歸打印目錄中的文件
if (*direntp->d_name != '.' && S_ISDIR(statbuf.st_mode)) {
// 當遞歸打印目錄的時候,需要指定絕對路徑,此時為 ./test
//sprintf(buf + strlen(buf), "/%s", direntp->d_name);
printf("***************************%s is directory!***********\n", buf);
printDir(buf);
printf("***************************%s ********************\n", buf);
}
}
}
note:以上問題是我在編程的時候遇到的,解決方法為經過百度和差異相關文獻(由於本人水平有限,可能解答有錯誤)。如有錯誤之處,請多多指正並諒解。