首页 > 其他分享 >目录操作

目录操作

时间:2024-04-07 23:25:42浏览次数:20  
标签:int errno argv char error 操作 目录

目录相关操作

获取当前工作目录getcwd

getcwd函数:获取当前工作目录的绝对路径

// manual
NAME
       getcwd, getwd, get_current_dir_name - get current working directory

SYNOPSIS
       #include <unistd.h>
    
       char *getcwd(char *buf, size_t size);

RETURN VALUE
On success, return a pointer to a string containing the pathname of the current working directory.
On failure, return NULL, and errno is set to indicate the error.

getcwd函数的惯用法

getcwd(NULL,0); 由内核申请合适的内存空间,保存当前工作目录的绝对路径
man手册中说明,使用这种方法时,需要调用者free内存空间

实现

#include <func.h>

int main(int argc, char* argv[])
{
    // ./getcwd
    // 检查参数
    if (argc != 1){
        error(1, 0, "Usage: %s path\n", argv[0]);
    }

    //获取当前工作目录
    char *cwd = getcwd(NULL, 0);
    if (!cwd) {
        error(1, erron, "getcwd %s", argv[1]);
    }
    
    puts(cwd);
    
    free(cwd);
    return 0;
]

改变当前的工作目录chdir

chdir函数:改变进程当前的工作目录

// manual
NAME
       chdir, fchdir - change working directory

SYNOPSIS
       #include <unistd.h>

       int chdir(const char *path);

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

实现

int main(int argc, char* argv[])
{
    // ./chdir
    // 修改进程的当前目录
    if (argc != 2) {
        error(1, 0, "Usage: %s path\n", argv[0]);
    }
    
    // 获取当前目录
    char *cwd = getcwd(NULL, 0);
    puts(cwd);

    // 修改目录权限
    int err = chdir(argv[1]);
    if (err){
        error(1, errno, "chdir");
    }

    cwd = getcwd(NULL, 0);
    puts(cwd);

    return 0;
}

创建目录mkdir

// manual
NAME
       mkdir, mkdirat - create a directory

SYNOPSIS
       #include <sys/stat.h>
       #include <sys/types.h>

       int mkdir(const char *pathname, mode_t mode);
RETURN VALUE
       return zero on success, or -1 if an error occurred (in which  case,  errno is set appropriately).

其中mode表示权限

0777: 目录的基本权限
0666: 文件的基本权限

实现

int main(int argc, char* argv[])
{
    // ./mkdir pathname [mode]
    // 创建目录
    if (argc != 3) {
        error(1, 0, "Usage: %s pathname [mode]\n", argv[0]);
    }
    
    // 参数解析
    mode_t mode;
    sscanf(argv[1], "%o", mode);

    // 创建目录
    int err = mkdir(argv[1], mode);
    if (err){
        error(1, errno, "mkdir %s\n", argv[1]);
    }

    return 0;
}

删除空目录rmdir

// manual
NAME
       rmdir - delete a directory

SYNOPSIS
       #include <unistd.h>

       int rmdir(const char *pathname);
RETURN VALUE
       On  success, zero is returned.  On error, -1 is returned, and errno is set appro‐priately.

实现

int main(int argc, char* argv[])
{
    // ./rmdir pathname
    // 删除空目录
    if (argc != 2) {
        error(1, 0, "Usage: %s pathname\n", argv[0]);
    }
    
    int err = rmdir(argv[1]);
    if (err) {
        error(1, errno, "rmdir %s\n", argv[1]);
    }
 
    return 0;
}

读取格式化输入 sscanf

sscanf函数可以将字符串以特定格式读入,可用于将字符串以特定格式写入到某个变量中

int sscanf(const char *str, const char *format, ...);
sscanf(argv[1], "%o", mode);

错误处理函数error

目录流相关操作

使用目录流,可以使开发人员在程序中有效访问和操作文件系统给的目录结构

目录流的基本单位是目录项

以下是目录流常见的功能:

  • opendir: 打开一个目录

  • closedir:关闭一个目录流

  • readdir: 读取下一个目录项

基本用不到系列:

  • telldir:返回目录流的当前位置

  • seekdir:移动到某个为止

  • rewinddir:移动到第一个

目录项

目录流是由一个个的目录项组成的,目录项的结构体如下:

struct dirent {
    ino_t          d_ino;       // inode number
    off_t          d_off;       // offset to the next dirent
    unsigned short d_reclen;    // length of this record
    unsigned char  d_type;      // type of file
    char           d_name[256]; // filename
};

在目录项中,至少要包含:d_ino d_name d_type

目录项的d_type

DT_BLK      This is a block device. 
DT_CHR      This is a character device. 
DT_DIR      This is a directory. 
DT_FIFO     This is a named pipe (FIFO). 
DT_LNK      This is a symbolic link. 
DT_REG      This is a regular file. 
DT_SOCK     This is a UNIX domain socket. 
DT_UNKNOWN  The file type could not be determined.

打开目录流opendir

// manual
NAME
       opendir, fdopendir - open a directory

SYNOPSIS
       #include <sys/types.h>
       #include <dirent.h>

       DIR *opendir(const char *name);
RETURN VALUE
       The opendir() functions return a pointer to the directory stream.
       On error, NULL is returned, and errno is set appropriately.

关闭目录流closedir

// manual
NAME
       closedir - close a directory

SYNOPSIS
       #include <sys/types.h>

       #include <dirent.h>

       int closedir(DIR *dirp);

RETURN VALUE
       The closedir() function returns 0 on success.  On error, -1 is returned, and  errno is set appropriately.

读取目录项readdir

readdir函数:用于读取一个目录项,并返回下一个目录项的dirp,直到为NULL或发生错误

NAME
       readdir - read a directory

SYNOPSIS
       #include <dirent.h>

       struct dirent *readdir(DIR *dirp);

DESCRIPTION
       The  readdir()  function returns a pointer to a dirent structure representing the
       next directory entry in the directory stream pointed to by dirp.  It returns NULL
       on reaching the end of the directory stream or if an error occurred.
           
RETURN VALUE
       On  success,  readdir() returns a pointer to a dirent structure.  (This structure
       may be statically allocated; do not attempt to free(3) it.)

       If the end of the directory stream is reached, NULL is returned and errno is  not
       changed.   If  an  error occurs, NULL is returned and errno is set appropriately.
       To distinguish end of stream from an error, set  errno  to  zero  before  calling
       readdir() and then check the value of errno if NULL is returned.

根据manual中描述,在使用readdir函数读取 direction entry 结束时,需要判断是否发生错误而导致退出

readdir函数的一个惯用法

errno = 0;
struct dirent *pcurr;
while ((pcurr = readdir(dirp)) != NULL) {
    // 具体操作...
}
if (errno) {
    error(1, errno, "readdir");
}

删除目录项

在Linux系统中,删除文件实际上是将硬链接数减1,可以使用unlink函数

// manual
NAME
       unlink - delete a name and possibly the file it refers to

SYNOPSIS
       #include <unistd.h>

       int unlink(const char *pathname);

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set
       appropriately.

目录和目录流使用场景

Linux系统中,一切皆文件。因此,目录可以看作是一种特殊的文件。它的内容是目录项的集合

如何正确使用针对目录和目录流的操作,就显得尤为重要。

目录操作:当涉及到对目录的增、删、查、改时使用。

目录流操作:遍历目录流时使用。

递归处理目录(重点)

递归处理目录时常用步骤

1. 打开目录流(opendir)
2. 遍历目录流
3. 关闭目录流

简易版的tree

实现tree类似于树的深度优先搜索。

需要注意的是,在实现青春版的tree时,需要忽略...,否则会导致死循环。

#include <func.h>

void dfs_print(const char *path, int width);
int directories, files;

int main(int argc, char* argv[])
{
    // ./tree dir
    if (argc != 2) {
        error(1, 0, "Usage: %s dir\n", argv[1]);
    }
    // 1. 打印目录信息
    puts(argv[1]);
    // 2. 遍历目录流, 递归打印每一个目录项
    dfs_print(argv[1], 4);
    // 3. 打印统计信息
    printf("%d directories, %d files\n", directories, files);
    return 0;
}

void dfs_print(char *path, int depths) {
    // 1. 打开文件流
    DIR *pdir = opendir(path);
    if (pdir == NULL) {
        error(1, erron, "opendir directory");
    }

    // 2. 遍历目录流, 打印目录项
    errno = 0;
    struct dirent *entry;
    while ((entry = readdir(pdir)) != NULL) {
        char *name = entry->d_name;
        // 忽略 . 和 ..
        if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
            continue;
        }
        
        // 打印目录项
        for (int i = 0; i < depths; i++) {
            printf(" ");
        }
        puts(name);

        // 递归遍历目录
        if (entry->d_type == DT_DIR) {
            directories++;
            // 拼接路径名
            char subpath[256];
            sprintf(subpath, "%s/%s", path, name );
            dfs_print(subpath, depths + 4);
        }
        else {
            files++;
        }
    }   // curr == NULL
    if (errno) {
        error(1, errno, "readdir");
    }

    // 3. 关闭目录流
    closedir(pdir);
}

递归复制目录

在实现递归复制目录时,不仅涉及到对目录的复制,还涉及到对文件的复制

#include <func.h>

void copyFile(const char* src, const char* dir);
void copyDir(const char* src, const char* dir);

int main(int argc, char* argv[])
{
    // ./copyDir src dst
    if (argc != 3){
        error(1, 0, "Usage: %s src dst", argv[0]);
    }
    copyDir(argv[1], argv[2]);
    return 0;
}

void copyFile(const char* src, const char* dst){
    // 复制文件
    // 1. 打开文件流
    int src_fd = open(src, O_RDONLY);
    if (src_fd == -1) {
        error(1, errno, "open %s", src);
    }

    int dst_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL);
    if (dst_fd == -1) {
        error(1, errno, "open %s", dst);
    }
    
    char buf[1024];
    int bytes;
    while ((bytes=read(src_fd, buf, sizeof(buf))) > 0){
        write(dst_fd, buf, bytes);
    }

    // 3. 关闭文件流
    close(src_fd);
    close(dst_fd);
    
    if (bytes < 0) {
        error(1, errno, "read");
    }
}

void copyDir(const char* src, const char* dst){
    // 创建dst目录
    int err = mkdir(dst, 0777);
    if (err) {
        error(1, errno, "mkdir %s", dst);
    }

    // 打开src目录
    DIR* psrc = opendir(src);
    if (!psrc) {
        error(1, errno, "opendir %s", src);
    }

    // 遍历目录流
    errno = 0;
    struct dirent* pcurr;
    while ((pcurr = readdir(psrc)) != NULL){
        //
        // 忽略.and..
        const char* name = pcurr->d_name;
        if (strcmp(name, ".")==0 || strcmp(name, "..") ==0) {
            continue;
        }
        
        char subsrc[256];
        char subdir[256];
        sprintf(subsrc, "%s/%s", src, name);
        sprintf(subdir, "%s/%s", dst, name);
        // 如果该目录项是目录,则调用copyDir递归复制目录
        if (pcurr->d_type == DT_DIR) {
            copyDir(subsrc, subdir);
        }

        // 如果该目录项是文件,则调用copyFile复制文件
        if (pcurr->d_type == DT_REG) {
            copyFile(subsrc, subdir);
        }
    }
    if (errno) {
       error(1, errno, "readdir"); 
    }

    // 3. 关闭文件流
    closedir(psrc);
}

递归删除目录

#include <func.h>

void deleteDir(const char *path){
    // 1. 打开目录流
    DIR *pdir = opendir(path);
    if (!pdir) {
        error(1, errno, "opendir %s", path);
    }

    // 2. 递归删除目录流
    errno = 0;
    struct dirent *curr;
    while ((curr = readdir(pdir)) != NULL) {
        // 忽略. and ..
        char *filename = curr->d_name;
        if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) {
            continue;
        }
        
        char subpath[256];
        sprintf(subpath, "%s/%s", path, filename);
        if (curr->d_type == DT_DIR) {
            deleteDir(subpath);
        }
        else {
            unlink(subpath);
        }
    }   // curr == NULL
    if (errno) {
        error(1, errno, "readdir");
    }
    
    // 删除当前目录
    rmdir(path);

    // 3. 关闭文件流
    closedir(pdir);
}

int main(int argc, char* argv[])
{
    // ./ deleteDir dir
    if (argc != 2) {
        error(1, 0, "Usage: %s dir\n", argv[0]);
    }
    // 删除目录
    deleteDir(argv[1]);
    return 0;
}

常用技巧

使用命令查看类型

以查看ino_t结构体为例

gcc -E .c文件 | grep -nE "ino_t"

标签:int,errno,argv,char,error,操作,目录
From: https://www.cnblogs.com/notob/p/18120140

相关文章

  • Linux核心目录及核心命令
    1.运维必备职场技能01:如何有效提问?1.1示范错误示范:可以适当客气,没有直奔主题。路人甲:李导,在吗?忙不忙?我有个问题 过了1个小时 李导996:在,请说。 过了2个小时路人甲:李导,我这里有个服务的故障,可以帮助我下吗? 过了1个小时 李导996:请讲路人甲:李导,有......
  • 最小化安装Ubutun后的初始化操作
    最小化安装Ubutun后的初始化操作由于Ubutub相关配置路径与红帽系操作系统有些差异,这边进行一些简单初始化记录。使用的操作系统镜像为:ubuntu-20.04.6-desktop-amd64.iso1.root账户配置使用该镜像安装系统,没有红帽系安装过程中对root用户的配置。需要进入系统后使用sudopassw......
  • 『Dynamo教程目录整理2023.01』BIM的乐趣By九哥
    你好,我是九哥~经常发现,很多小伙伴问的问题,其实以前文章里都讲过,所以为了方便小伙伴们查找和学习,我将公众号里的Dynamo相关文章整理了出来,查找资料就不用来回的翻历史记录了~一、基础教程Dynamo初学常识梳理Dynamo初学常识梳理(二)Dynamo初学常识梳理(三)——节点Dyna......
  • C++ Break、Continue 和 数组操作详解
    C++Break和Continuebreak语句还可以用来跳出循环。在以下示例中,当i等于4时跳出循环:for(inti=0;i<10;i++){if(i==4){break;}cout<<i<<"\n";}C++Continue以下示例跳过了值为4的情况:for(inti=0;i<10;i++){if(i==4)......
  • python 字符串的操作
    #字符串拼接str1="Hello"str2="World"combined_str=str1+""+str2print(combined_str)#字符串重复str1="Python"repeated_str=str1*3print(repeated_str) #根据字符串索引取值str1="Hello"char=str1[1]#......
  • make编译报错:fatal error: filesystem: 没有那个文件或目录 #include <filesystem>
    报错:fatalerror:filesystem:没有那个文件或目录#include(filesystem)解决方法一:修改头文件#include<experimental/filesystem>添加依赖在编译时,后面添加:-lstdc++fs编译通过。解决方法二:升级gcc升级到gcc-8或8以上问题即可解决:添加PPA存储库首先,您需要添加Ub......
  • 【C语言】文件操作(打开,关闭,写入,读取,指针位置)
    文件操作的函数在标准库stdio.h中。#include<stdio.h>1、fopen,fclose 打开文件,关闭文件fopen:打开文件。fclose:关闭文件。补充:perror:输出errno对应的错误信息。fopen:  FILE *fopen(constchar*filename,constchar*mode)参数:filename是字符串(要打开的文......
  • 关于.gitignore怎么保留子目录的子目录这件事
    起因最近遇到了个需求,就是需要在.gitignore里面保留子目录下的子目录的所有文件过程然而,当你打开网上的教程,会发现所有的教程只会教你子目录,或者是打着指定目录的名堂本质上也只有子目录可行:Git忽略目录,只保留指定目录.gitignore保留忽略目录下的指定文件夹_gitignore只跟踪......
  • 实验人员A在操作过程中不慎将装有4L乙腈的瓶子打碎,请简述此时应采取的应急处理措施
    实验人员A在操作过程中不慎将装有4L乙腈的瓶子打碎,应立即按照实验室化学品泄漏应急处理方案以及乙腈特定的应急处置指南来行动。以下是针对这一紧急情况应采取的应急处理措施:人身安全优先:撤离现场:实验人员A应立即停止所有操作,避免直接接触泄漏物,并迅速离开泄漏区域。同时,通知......
  • C语言文件操作
    本篇文章从文件是什么,为什么使用文件,到怎么使用文件来介绍文件。一.文件是什么?磁盘(硬盘)上的文件是文件。但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。1.文件名文件名包含3部分:文件路径+文件名主干+文件按后缀。2.程序文件程序......