Linux 文件系統操作詳解:目錄操作與文件操作(實戰:tree命令實現)

一.目錄操作

1.獲取、改變、創建、刪除當前目錄

  • char* getcwd(char*, size_t)
  • int chdir(char*, mode_t)

創建和刪除目錄

  • int mkdir(char*, mode_t)
  • int rmdir(char*)
void test1()
{
    cout << "Now working directory is :" << endl;
    cout << getcwd(NULL, 0) << endl;
    if (mkdir("./newdir", 0775))
        cout << "mkdir ERROR" << endl;
    if (chdir("./newdir/"))
        cout << "can't find the directory" << endl;
    cout << getcwd(NULL, 0) << endl;

    if (chdir(".."))
        cout << "can't find the directory" << endl;
    if (rmdir("newdir"))
        cout << "rmdir ERROR" << endl;
}


2.打開目錄流、讀取目錄流、關閉目錄流

  • DIR* opendir(char*)
  • struct dirent* readdir(char*)
  • int closedir(DIR*)
    整個與FILE*的文件流十分相似,也很好理解。
    struct dirent結構體用於存儲目錄下讀取到的文件信息。
    通過下面的操作,可以將newdir目錄下的文件全部讀取出來。
void test2()
{
    DIR *dir = opendir("newdir");
    if (!dir)
        cout << "opendir ERROR" << endl;
    struct dirent *dir_info;
    while (dir_info = readdir(dir))
        cout << dir_info->d_name << endl;
    closedir(dir);
}

3.讀取文件的類型,大小, 最近訪問時間等

  • int stat(const char*, struct stat*)
  • int fstat(int fd, struct stat*)

struct stat 中最常用的兩個字段:

  • st_mode 文件類型
  • st_size 文件大小

針對st_mode的判斷有以下幾種:

  • S_ISREG(st_mode) 是否爲普通文件
  • S_ISDIR(st_mode) 是否爲目錄
void test3()
{
    struct stat stat_info;
    cout << getcwd(NULL, 0) << endl;
    if (stat("newdir", &stat_info))
        cout << "stat ERROR" << endl;

    if (S_ISREG(stat_info.st_mode))
        cout << "file" << endl;
    else if (S_ISDIR(stat_info.st_mode))
        cout << "dir" << endl;

    cout << stat_info.st_size << endl;

    cout << stat_info.st_uid << endl;

    cout << stat_info.st_gid << endl;

    cout << "輸出文件的最近依次的訪問時間:" << endl;
    //MM DD HH:mm
    time_t rawtime = stat_info.st_atime;
    struct tm *timeinfo = localtime(&rawtime);
    if (timeinfo->tm_min > 9)
        cout << timeinfo->tm_mon + 1 << "月 " << timeinfo->tm_mday << "日 " << timeinfo->tm_hour << ":" << timeinfo->tm_min << endl;
    else
        cout << timeinfo->tm_mon + 1 << "月 " << timeinfo->tm_mday << "日 " << timeinfo->tm_hour << ":0" << timeinfo->tm_min << endl;

}

4.綜合練習:實現 tree 命令

以樹形結構輸出該目錄下的文件
採用DFS的方法輸出

void tree(const char *dirname, int depth)
{
    DIR *dir = opendir(dirname);
    if (!dir)
        return;

    chdir(dirname);
    struct dirent *dirbuf;
    while (dirbuf = readdir(dir))
    {
        if (!strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, ".."))
            continue;
        else
        {
            cout << "|" << setw(depth) << setfill(' ') << "--" << dirbuf->d_name << endl;
            tree(dirbuf->d_name, depth + 5);
        }
    }
    chdir("..");
    closedir(dir);
}

void test4()
{
    tree("newdir", 0);
}

系統tree輸出:

wwx@VM-0-7-ubuntu:~/Linux/Day04/fileSystem/newdir$ tree
.
|-- dir1
|   `-- dir1file
|-- dir2
|   `-- dir2file
|-- file1
`-- file2

2 directories, 4 files

程序輸出:

wwx@VM-0-7-ubuntu:~/Linux/Day04/fileSystem$ cd "/home/wwx/Linux/Day04/fileSystem/" && g++ main1.cpp -o main1 && "/home/wwx/Linux/Day04/fileSystem/"main1
|--dir1
|   --dir1file
|--file1
|--file2
|--dir2
|   --dir2file

二.文件操作

在linux環境下,一般來操作文件都是通過操作文件描述符(文件句柄)來進行操作。

1.打開關閉文件

  • int open(const char*, int , mode_t)
  • int close(int)

2.讀取,寫入文件

  • size_t read(int fd, char* buf, size_t size)
  • size_t write(int fd, char* buf, size_t size)

練習:從file1中讀取, 再寫到file2中

void test1()
{
    int fd1 = open("file1", O_RDWR | O_CREAT, 0755);
    int fd2 = open("file2", O_RDWR | O_CREAT, 0755);
    if (fd1 == -1)
        cout << "fd1 open ERROR" << endl;
    if (fd2 == -1)
        cout << "fd2 open ERROR" << endl;
                
    char buf[1024] = {0};
    int len = 0;    
    while ((len = read(fd1, buf, sizeof(buf))) > 0)
    {
        cout << len << endl;
        write(fd2, buf, len);
        memset(buf, 0, sizeof(buf));
    }

    close(fd1);
    close(fd2);
}

3. 改變文件大小

  • int ftruncate(int fd, off_t length)
    將文件從length偏移的字節數開始截斷
void test3()
{
    int fd = open("file1", O_RDWR);
    ftruncate(fd, 1000);
    close(fd);
}

4.文件定位 與 文件空洞設計

  • off_t lseek(int fd, off_t offset, int whence)

whence:

  • SEEK_SET
  • SEEK_CUR
  • SEEK_END

文件空洞設計一般用於共享內存

//文件空洞設計
void test4()
{
    int fd = open("file3", O_RDWR | O_CREAT, 0755);
    lseek(fd, 1024, SEEK_SET);
    write(fd, "HelloWorld", strlen("HelloWorld"));
    close(fd);
}

5.文件描述符的複製

文件描述符類比智能指針的引用計數,也可以理解成深拷貝

  • int dup(int oldfd)
    此種方式是自動分配文件描述符(從小到大)
    當使用 int fd2 = fd; 語句時, 如果進行 close(fd), 那麼fd, fd2指向的文件會立刻關閉
    當使用 int fd2 = dup(fd); 語句時, 如果進行了 close(fd), fd2指向的文件因爲引用計數仍爲1,所以文件不會關閉

  • int dup2(int oldfd, int newfd)
    此種方式是自己定義新的文件描述符(newfd)

void test5()
{
    int fd = open("file4", O_RDWR | O_CREAT, 0755);
    cout << "fd:" << fd << endl;
    // int fd2 = fd;
    int fd2 = dup(fd);
    cout << "fd2:" << fd2 << endl;
    close(fd);
    char buf[1024];
    read(fd2, buf, sizeof(buf));
    cout << buf << endl;
}

6.標準輸入、輸出、錯誤輸出

分別代表文件描述符中的0、1、2
關閉文件描述符 1 之後,fd2分配到的就是輸出 1 , 那麼無論我們輸出什麼到輸出流中,都不會在屏幕中的顯示,而是寫在fd2指向的文件中

void test6()
{
    int fd = open("file6", O_RDWR|O_CREAT, 0755);
    cout << "fd:" << fd << endl;
    close(1);//關閉輸出
    int fd2 = dup(fd);
    cout << "fd2:" << fd2 << endl;
    cout << "HelloWorld" << endl;
    close(fd);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章