首页 > 编程语言 >系统编程(常见指令实现)

系统编程(常见指令实现)

时间:2025-01-11 13:05:35浏览次数:3  
标签:info 文件 编程 int 常见 strcat char 指令 文件夹

ls -l完整实现

1. 文件操作

  • opendir(): 用于打开一个目录流,并返回一个指向DIR结构的指针。如果打开失败,返回NULL。
函数名opendir
头文件#include<sys/types.h>#include<dirent.h>
函数原型DIR * opendir(const char * name);
功能打开 name 指定的目录
参数说明name:要操作的目录名;2. DIR * 详细内容请查看相关数据结构体 说明部分
返回值成功,返回DIR* 目录流,否则返回NULL,并将错误码存放于errno 里
  • readdir(): 读取目录流中的下一个目录项。每次调用时,它返回一个指向struct dirent结构的指针,该结构包含了目录项的信息。
函数名readdir
头文件#include<sys/types.h>#include<dirent.h>
函数原型struct dirent * readdir(DIR * dir);
功能遍历文件目录
参数说明1.dir:要操作的目录流指针;struct dirent * 详见相关数据结构体说明部分
返回值成功:用于描述一个目录项信息的指针,隐藏的位置指针会指向下一个目录项错误发生或读取到目录尾则返回NULL并将错误码存入 errno 中
  • closedir(): 关闭由opendir()打开的目录流。
函数名closedir
头文件#include<sys/types.h> #include<dirent.h>
函数原型int closedir(DIR *dir);
功能关闭参数dir所指的目录流
参数说明dir:要操作的目录流指针;
返回值成功,返回0,失败返回-1,并将错误码放入 errno
  • struct dirent: 这是一个结构体,包含了目录项的信息,如文件名(d_name)和文件类型(d_type)。
 struct __dirstream
    {
       void     *__fd;             /* `struct hurd_fd' pointer for descriptor.   */
        char     *__data;                 /* Directory block.   */
        int     __entry_data;  /* Entry number `__data' corresponds to.   */
        char     *__ptr;                   /* Current pointer into the block.   */
        int       __entry_ptr;     /* Entry number `__ptr' corresponds to.   */
        size_t  __allocation;           /* Space allocated for the block.   */
        size_t  __size;                      /* Total valid data in the block.   */
        __libc_lock_define (, __lock)   /* Mutex lock for this structure.   */
    };
          
    typedef struct __dirstream DIR; 

struct dirent
    {
        long  d_ino;              /* 索引节点号 */   
        off_t d_off;               /* 在目录文件中的偏移 */   
        unsigned short d_reclen;    /* 文件纪录长度 */   
        unsigned char  d_type;        /* 文件类型 */   
        char  d_name[NAME_MAX+1]; /* 文件名 */
    } 

2. 文件类型判断

  • d_type: 在struct dirent结构体中,d_type字段用于表示文件类型。常见的类型有DT_REG(普通文件)、DT_DIR(目录)、DT_LNK(符号链接)等。注意,直接使用数字(如4)来判断文件类型是不推荐的,应该使用宏定义(如DT_DIR)来提高代码的可读性和可移植性。
enum
{
    DT_UNKNOWN = 0,      //未知
    DT_FIFO = 1,                 //管道文件
    DT_CHR = 2,                  //字符设备文件
    DT_DIR = 4,                   //目录文件
    DT_BLK = 6,                   //块设备文件
    DT_REG = 8,                  //一般文件
    DT_LNK = 10,                 //链接文件
    DT_SOCK = 12,              //套接字文件 
    DT_WHT = 14                 
};
 注意: d_type 虽然是个unsigned char 
               类型,但实际取值如上

3. 文件状态信息的获取

  • stat(): 该函数用于获取文件的状态信息,并将这些信息填充到一个stat结构体中。这个结构体包含了文件的多种属性,如大小(st_size)、权限(st_mode)、链接数(st_nlink)、最后修改时间(st_mtime)等。
struct stat 
{
    dev_t          st_dev;                  //文件设备编号
    ino_t          st_ino;                   //文件inode节点号
    mode_t      st_mode;               //文件类型,访问权限等
    nlink_t       st_nlink;                //文件的连接数
    uid_t          st_uid;                   //文件所有者的用户ID
    gid_t          st_gid;                  //文件所有者对应的组ID
    dev_t         st_rdev;         //若文件为设备文件,则表示设备编号
    off_t          st_size;           //文件大小,对应的文件字节数
    blksize_t    st_blksize;        //文件系统的 I/O 缓冲区大小
    blkcnt_t     st_blocks;    //占用文件区块数量,每一区块512 字节
    time_t        st_atime;       //文件最后一次被访问的时间
    time_t        st_mtime;    //文件内容最后一次被修改的时间
    time_t        st_ctime;   //最后一次改变时间(属性改变)
};

函数名stat
头文件#include <fcntl.h> #include <unistd.h>#include <sys/types.h> #include <sys/stat.h>
函数原型int stat(const char path, struct stat buf)int fstat(int fd, struct stat *buf);
功能获取文件状态,
参数说明path:要操作的文件名或路径;buf:指向stat 结构体的指针,用来获取文件状态信息:3. fd:文件描述符 注:struct stat 见下页 stat 函数用来获取未打开的文件状态信息,fstat 函数用来获取打开的文件状态信息
返回值成功:返回 0出错:返回 -1,并将错误码存入 errno 中
  • S_ISDIR(): 这是一个宏,用于判断文件是否为目录。它接受一个文件模式(mode)作为参数,如果文件是目录,则返回非零值。

4. 时间处理

  • time(): 获取当前时间(自1970年1月1日以来的秒数)。
  • localtime(): 将time_t类型的时间转换为本地时间(struct tm类型)。
  • struct tm: 这是一个结构体,包含了时间的详细信息,如年(tm_year)、月(tm_mon)、日(tm_mday)、小时(tm_hour)、分钟(tm_min)等。

5. 字符串操作

  • strcpy(): 复制字符串。
  • strcat(): 连接字符串。
  • strtok(): 分割字符串。它根据指定的分隔符来分割字符串,并返回分割后的子字符串。
  • snprintf(): 格式化字符串并输出到指定的字符数组中。与sprintf()相比,snprintf()允许指定输出缓冲区的大小,从而避免了缓冲区溢出的风险。
  • memset(): 将一段内存区域的内容全部设置为指定的值(通常是0)。

6. 错误处理

  • perror(): 打印描述最近一次库函数调用错误的字符串。它通常与检查函数返回值是否为NULL或-1等错误条件一起使用。

7. 注意事项

  • 缓冲区大小: 在使用字符串操作函数(如snprintf()、strcat())时,要注意缓冲区的大小,以避免缓冲区溢出。
  • 宏定义与数字: 在判断文件类型时,应使用宏定义(如DT_DIR)而不是数字,以提高代码的可读性和可移植性。
  • 错误处理: 在进行文件操作时,要检查函数的返回值,以处理可能的错误情况。
header.h
#ifndef _HEARD_H
#define _HEARD_H

// 常用头文件, remove, rename
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// access, read, write, lseek, stat,lstat, rmdir, fork
#include <unistd.h>
// open, lseek, stat,lstat, getpwuid, opendir, closedir, mkdir,
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// mmap, munmap
#include <sys/mman.h>

// getpwuid
#include <pwd.h>
#include <grp.h>
// time
#include <time.h>

// opendir,readdir, closedir
#include <dirent.h>

typedef struct stat Stat;
// 1.获取文件的类型
int getFileType(Stat *pstat, char *info);
// 2.获取文件所属用户的权限
int getOwnerPrem(Stat *pstat, char *info);
// 3.获取同组其他用户的权限
int getgrPrem(Stat *pstat, char *info);
int getgrrPrem(Stat *pstat, char *info);
// 5.获取链接数量
int getLinkNum(Stat *pstat, char *info, const char *file);
// 6.获取文件的所属名称
int getOwnerName(Stat *pstat, char *info);
// 7.文件所属用户的组名
int getGroupName(Stat *pstat, char *info);
// 8.获取文件的大小
int getFileSize(Stat *pstat, char *info);
// 9.获取文件最后修改的时间
int getModifyTime(Stat *pstat, char *info);
// 10.获取文件的名称
int getFileName(const char *filePath, char *info);
int getOwnerGroupName(Stat *pstat, char *info);
#endif
myls.c

#include "header.h"

// 1.获取文件的类型
int getFileType(Stat *pstat, char *info)
{
    // 获取结构体成员的值
    mode_t mode = pstat->st_mode;
    printf("mode: %o\n", mode);
    // 判断文件类型
    if (S_ISLNK(mode))
        info[0] = 'l';
    else if (S_ISREG(mode))
        info[0] = '-';
    else if (S_ISDIR(mode))
        info[0] = 'd';
    else if (S_ISCHR(mode))
        info[0] = 'c';
    else if (S_ISBLK(mode))
        info[0] = 'b';
    else if (S_ISSOCK(mode))
        info[0] = 's';
    else if (S_ISFIFO(mode))
        info[0] = 'p';
    return 0;
}

int getOwnerPrem(Stat *pstat, char *info)
{
    // 获取结构体成员的值
    mode_t mode = pstat->st_mode; // 40542
    /*
     100 000 101 100 010
&    000 000 111 000 000
---------------------------
     000 000 101  000 000
     0   0   5    0   0
    */
    // 判断文件类型
    // int perm = mode & 00700;   drwx0000000000000000
    int perm = mode & S_IRWXU;
    if (perm == S_IRWXU)
        strcat(info, "rwx");
    else if (perm == S_IRUSR)
        strcat(info, "r--");
    else if (perm == S_IWUSR)
        strcat(info, "-w-");
    else if (perm == S_IXUSR)
        strcat(info, "--x");
    else if (perm == (S_IRUSR | S_IWUSR))
        strcat(info, "rw-");
    else if (perm == (S_IXUSR | S_IWUSR))
        strcat(info, "-wx");
    else if (perm == (S_IXUSR | S_IRUSR))
        strcat(info, "r-x");
    else
        strcat(info, "---");
    return 0;
}

// 用户组权限
int getgrPrem(Stat *pstat, char *info)
{
    // 获取结构体成员的值
    mode_t mode = pstat->st_mode; // 40542
    printf("用户组权限\n");
    int perm = mode & S_IRWXG; // 获取用户组权限位
    switch (perm)
    {
    case S_IRWXG:
        strcat(info, "rwx");
        break;
    case S_IRGRP:
        strcat(info, "r--");
        break;
    case S_IWGRP:
        strcat(info, "-w-");
        break;
    case S_IXGRP:
        strcat(info, "--x");
        break;
    case (S_IRGRP | S_IWGRP):
        strcat(info, "rw-");
        break;
    case (S_IXGRP | S_IWGRP):
        strcat(info, "-wx");
        break;
    case (S_IXGRP | S_IRGRP):
        strcat(info, "r-x");
        break;
    default:
        strcat(info, "---");
        break;
    }

    // 返回一些值,这里假设返回1表示成功
    return 1;
}
// 其他用户
int getgrrPrem(Stat *pstat, char *info)
{
    // 获取结构体成员的值
    mode_t mode = pstat->st_mode; // 40542
    printf("其他用户权限\n");
    int perm = mode & S_IRWXO; // 获取用户组权限位
    switch (perm)
    {
    case S_IRWXO:
        strcat(info, "rwx");
        break;
    case S_IROTH:
        strcat(info, "r--");
        break;
    case S_IWOTH:
        strcat(info, "-w-");
        break;
    case S_IXOTH:
        strcat(info, "--x");
        break;
    case (S_IROTH | S_IWOTH):
        strcat(info, "rw-");
        break;
    case (S_IXOTH | S_IWOTH):
        strcat(info, "-wx");
        break;
    case (S_IXOTH | S_IROTH):
        strcat(info, "r-x");
        break;
    default:
        strcat(info, "---");
        break;
    }

    // 返回一些值,这里假设返回1表示成功
    return 1;
}

// 6.获取文件的所属名称
int getOwnerName(Stat *pstat, char *info)
{
    // 获取所属用户的uid
    uid_t uid = pstat->st_uid;
    // 调用函数通过用户id获取用户名称
    struct passwd *passwd_p = getpwuid(uid);
    // 获取结构体成员中的name
    char *uname = passwd_p->pw_name;
    strcat(info, "  ");
    // 将用户名拼接到字符串中
    strcat(info, uname);
    return 0;
}
// 7.获取文件所属组名称
int getOwnerGroupName(Stat *pstat, char *info)
{
    // 获取所属组的gid
    gid_t gid = pstat->st_gid;
    // 调用函数通过组id获取组信息
    struct group *grp_p = getgrgid(gid);
    if (grp_p == NULL)
    {
        strcat(info, "  [unknown group]");
        return -1; // 返回错误码表示失败
    }
    // 获取结构体成员中的组名
    char *gname = grp_p->gr_name;

    // 在info字符串中添加空格和组名
    strcat(info, "  ");
    strcat(info, gname);

    return 0;
}
// 获取文件夹中子的数量
int getSubCount(const char *file)
{
    // 统计变量
    int count = 0;
    // 打开文件夹
    DIR *dir = opendir(file);
    if (dir == NULL)
    {
        perror("opendir failed");
        return -1;
    }
    // 遍历读取子
    struct dirent *subDir = NULL;
    while (subDir = readdir(dir))
    {
        // 判断子是文件夹
        if (subDir->d_type == 4)
        // if (subDir->d_type == DT_DIR)
        {
            // 统计子文件夹的数量
            count++;
        }
    }
    // 关闭文件夹
    closedir(dir);
    return count;
}

// 5.获取链接数量: 文件显示子文件夹的数量,文件显示硬连接的数量
int getLinkNum(Stat *pstat, char *info, const char *file)
{
    // 判断文件的类型是不是文件夹
    if (S_ISDIR(pstat->st_mode))
    {
        // 是文件夹,获取子文件夹的数量--稍后实现
        int count = getSubCount(file);
        if (count == -1)
        {
            return -1;
        }
        char buf[10] = {0};
        snprintf(buf, 10, " %d ", count);
        strcat(info, buf);
    }
    else
    {
        // 是文件,获取文件的链接数量
        nlink_t num = pstat->st_nlink;
        // 将基本类型的数据转换成字符串用sprintf
        // 定义字符数组用户存储转换后的结果
        char buf[10] = {0};
        // sprintf(buf, "num=%lu", num);   相比snprintf不安全,有越界的风险
        snprintf(buf, 10, " %lu ", num);
        strcat(info, buf);
    }
    return 0;
}

// 8.获取文件的大小
int getFileSize(Stat *pstat, char *info)
{
    off_t size = pstat->st_size;
    // 定义字符数组用户存储转换后的结果
    char buf[10] = {0};
    snprintf(buf, 10, " %ld ", size);
    strcat(info, buf);
    return 0;
}

// 9.获取文件最后修改的时间: 如果是当年的就显示时间,如果是往年的就显示年份
int getModifyTime(Stat *pstat, char *info)
{
    // 获取当前时间
    time_t curTime = time(NULL);
    // 根据当前秒值获取日历时间
    struct tm *tm = localtime(&curTime);
    // 获取当前的年份
    int curYear = tm->tm_year;

    // 从文件状态中获取最后修改的时间
    time_t modifyTime = pstat->st_mtime;
    struct tm *m_tm = localtime(&modifyTime);
    // 定义字符串用于拼接日期和时间
    char buf[20] = {0};
    // 判断是否是当年
    if (m_tm->tm_year == curYear)
    {
        // 当年: x月  x日  hh:mm
        snprintf(buf, 20, " %d月  %d日  %02d:%02d ", m_tm->tm_mon + 1, m_tm->tm_mday, m_tm->tm_hour, m_tm->tm_min);
    }
    else
    {
        // 往年:x月  x日  yyyy
        snprintf(buf, 20, " %d月  %d日 %d ", m_tm->tm_mon + 1, m_tm->tm_mday, m_tm->tm_year + 1900);
    }
    // 将日期追加到info中
    strcat(info, buf);

    return 0;
}

// 10.获取文件的名称 ~/aa.txt
int getFileName(const char *filePath, char *info)
{
    // 因为filePath被const修饰,并且这个字符串是控制台传入的,所以它具备不可修改的特性
    // 所以定义数组转存文件路径
    char str[100] = {0};
    strcpy(str, filePath);
    // 截取字符串--strtok
    char *subStr = strtok(str, "/");
    // 定义数组存储截取到的字符串
    char lastStr[50] = {0};
    while (subStr != NULL)
    {
        memset(lastStr, 0, 50);
        strcpy(lastStr, subStr);
        subStr = strtok(NULL, "/");
    }
    // lastStr中存储的就是文件名
    strcat(info, "  ");
    strcat(info, lastStr);
}
main.c
#include <stdio.h>
#include "header.h"

// 添加一个函数来遍历目录并打印每个文件的信息
void print_directory_contents(const char *dirpath)
{
    DIR *dir = opendir(dirpath);
    if (!dir)
    {
        perror("opendir failed");
        return;
    }

    struct dirent *entry;
    char path[1024];
    struct stat statbuf;

    while ((entry = readdir(dir)) != NULL)
    {
        // 跳过 "." 和 ".." 目录项
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
        {
            continue;
        }

        snprintf(path, sizeof(path), "%s/%s", dirpath, entry->d_name);

        // 使用lstat而不是stat,因为我们要保留符号链接的信息
        if (lstat(path, &statbuf) == -1)
        {
            perror("lstat failed");
            continue;
        }

        // 为每个文件分配一个足够大的缓冲区来存储信息
        char info[256] = {0};

        // 获取并打印文件信息
        getFileType(&statbuf, info);
        getOwnerPrem(&statbuf, strcat(info, " ")); // 注意:这里使用strcat来追加字符串,确保info有足够的空间
        // ... (省略其他权限和信息的获取,因为它们应该类似于下面的示例)
        getLinkNum(&statbuf, strcat(info, " "), path);
        getOwnerName(&statbuf, strcat(info, " "));

        // 3.获取同组用户的权限---自学
        getgrPrem(&statbuf, strcat(info, " "));
        // 4.获取不同组的其他用户权限---自学
        getgrrPrem(&statbuf, strcat(info, " "));

        getOwnerName(&statbuf, strcat(info, " "));
        // 8.获取文件的大小
        getFileSize(&statbuf, strcat(info, " "));

        // 9.获取文件最后修改的时间
        getModifyTime(&statbuf, strcat(info, " "));

        // 获取并追加文件名(应该在最后做,因为它会覆盖info缓冲区的末尾部分)
        getFileName(entry->d_name, info + strlen(info)); // 这里不需要再strcat一个空格,因为getFileName应该自己处理

        // 打印文件信息
        puts(info);
    }

    closedir(dir);
}

int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        puts("缺少参数");
        return -1;
    }

    print_directory_contents(argv[1]);

    return 0;
}

在这里插入图片描述

rm -r 实现

1. 文件系统操作

opendir 函数
  • 原型DIR *opendir(const char *name);
  • 功能:打开一个目录流,并返回一个指向 DIR 结构体的指针,用于后续读取目录内容。
  • 参数name 是目录的路径。
  • 返回值:成功时返回指向 DIR 结构体的指针,失败时返回 NULL
readdir 函数
  • 原型struct dirent *readdir(DIR *dirp);
  • 功能:从目录流 dirp 中读取下一个目录项。
  • 参数dirpopendir 返回的目录流指针。
  • 返回值:指向 dirent 结构体的指针,包含目录项的信息。如果到达目录末尾或出错,返回 NULL
closedir 函数
  • 原型int closedir(DIR *dirp);
  • 功能:关闭目录流。
  • 参数dirpopendir 返回的目录流指针。
  • 返回值:成功时返回 0,失败时返回 -1
rmdir 函数
  • 原型int rmdir(const char *pathname);
  • 功能:删除一个空目录。
  • 参数pathname 是要删除的目录的路径。
  • 返回值:成功时返回 0,失败时返回 -1
函数名rmdir
头文件#include <unistd.h>
函数原型int rmdir(const char * dirname);
功能删除一个空目录
参数说明dirname:要操作的目录名;
返回值成功:返回0出错:返回 -1,并将错误码存入 errno 中
remove 函数
  • 原型int remove(const char *pathname);
  • 功能:删除一个文件。
  • 参数pathname 是要删除的文件的路径。
  • 返回值:成功时返回 0,失败时返回 -1
函数名remove
头文件#include <stdio.h>
函数原型int remove(const char * pathname);
功能删除一个文件或者目录,当pathname为一个文件则调用unlink来删除,如果是一个目录,则调用rmdir 来删除。
参数说明pathname:要操作的文件或者目录名;
返回值成功:返回0出错:返回 -1,并将错误码存入 errno 中

2. 字符串操作

strcmp 函数
  • 原型int strcmp(const char *s1, const char *s2);
  • 功能:比较两个字符串。
  • 参数s1s2 是要比较的两个字符串。
  • 返回值:若 s1s2 字符串相等,则返回 0;若 s1 小于 s2,则返回负数;若 s1 大于 s2,则返回正数。
snprintf 函数
  • 原型int snprintf(char *str, size_t size, const char *format, ...);
  • 功能:将格式化的数据写入字符串。
  • 参数str 是目标字符串的指针,size 是目标字符串的大小,format 是格式化字符串,... 是可变参数列表。
  • 返回值:写入的字符数(不包括终止的空字符)。如果返回值大于或等于 size,则输出字符串被截断。

3. 递归算法

递归是一种在函数内部调用自身的编程技巧。在这个例子中,myrm 函数使用递归算法来删除目录及其所有内容。递归的基本思想是将问题分解为更小的子问题,直到达到一个简单的情况(递归基准情况),可以直接解决。在这个例子中,递归基准情况是目录为空,这时可以直接删除目录。对于非空目录,函数会遍历目录中的每个条目,如果是文件则删除文件,如果是子目录则递归调用自身删除子目录

mian.c
#include <stdio.h>
#include "header.h"

// 定义函数:判断指定的文件夹是否为空
// 返回0(假)表示文件夹不为空, 1(真)表示文件夹为空
int dirIsEmpty(char const *dir)
{
    int flag = 1;
    // 打开文件夹
    DIR *dir_p = opendir(dir);
    // 循环读取文件夹中的子
    struct dirent *p = NULL;
    while (p = readdir(dir_p))
    {
        // 过滤文件夹中的两个隐藏文件夹:.和..
        if (strcmp(p->d_name, ".") == 0 || strcmp(p->d_name, "..") == 0)
        {
            continue;
        }
        flag = 0;
        break;
    }
    closedir(dir_p);
    return flag;
}

// 定义函数:根据给定文件夹的路径递归删除文件夹中的所有内容
void myrm(char const *dir)
{
    // 递归的出口---当文件夹为空
    if (dirIsEmpty(dir))
    {
        // 文件夹为空,删除当前文件夹,并返回
        rmdir(dir);
        return;
    }
    // 递归的规律: 当前文件夹中的子是文件还是文件夹,如果是文件就删除,如果是文件夹就递归调用
    // 打开当前文件夹
    DIR *dir_p = opendir(dir);
    struct dirent *p = NULL;
    while (p = readdir(dir_p))
    {
        // 过滤文件夹中的两个隐藏文件夹:.和..
        if (strcmp(p->d_name, ".") == 0 || strcmp(p->d_name, "..") == 0)
        {
            continue;
        }
        // 判断当前子是文件还是文件夹
        //  if (p->d_type == DT_DIR)
        if (p->d_type == 4)
        {
            // 子是文件夹
            char sub_dir[100] = {0};
            snprintf(sub_dir, 100, "%s/%s", dir, p->d_name);
            // 递归调用
            myrm(sub_dir);
        }
        else
        {
            // 子是文件,就直接删除
            // 拼装要删除的文件的路径
            char del_path[100] = {0};
            snprintf(del_path, 100, "%s/%s", dir, p->d_name);
            // 调用unlink删除即可
            unlink(del_path);
        }
    }
    // 关闭文件夹
    closedir(dir_p);
    //删除当前空文件夹
    rmdir(dir);
}

int main(int argc, char const *argv[])
{
    // 通过命令传入文件夹的路径
    if (argc < 2)
    {
        puts("缺少参数");
        return -1;
    }
    puts(argv[1]);

    // 调用stat/lstat获取文件的类型
    Stat stat = {0};
    lstat(argv[1], &stat);
    // 判断用户输入的是文件路径还是文件夹路径
    if (!S_ISDIR(stat.st_mode))
    {
        // 不是文件夹,就一定是文件,是文件就直接删除
        int r = unlink(argv[1]);
        if (r == -1)
        {
            perror("unlink--delete file failed");
            return -1;
        }
        return 0;
    }

    // 如果是文件夹
    myrm(argv[1]);

    return 0;
}

在这里插入图片描述

多级文件夹

  1. 字符串操作
    strcpy 函数:用于将源字符串复制到目标字符串数组中。这里它将命令行参数提供的路径复制到 dir_path 数组中。
    strtok 函数:用于将字符串分割成一系列标记(token)。这里它根据 “/” 分隔符将路径分割成各级目录名。
    snprintf 函数:用于将格式化的数据写入字符串。这里它用于将目录名与 “/” 结合,形成完整的目录路径。
    strcat 函数:用于将两个字符串连接起来。这里它将逐级形成的目录路径连接起来,形成完整的要创建的目录路径。
  2. 文件系统操作
    access 函数:用于检查调用进程对文件的访问权限。这里它用于检查某个目录路径是否存在(通过 F_OK 参数)。
    mkdir 函数:用于创建一个新目录。这里它在确认某级目录不存在后,用于创建该目录。
  3. 错误处理
    perror 函数:用于输出描述最近一次库函数调用错误的字符串。这里它在 mkdir 调用失败时被调用,以输出错误信息。
    返回值检查:代码通过检查系统调用的返回值来判断操作是否成功,并在失败时返回 -1。
  4. 命令行参数
    argc 和 argv:argc 表示命令行参数的数量,argv 是一个字符串数组,存储了所有的命令行参数。这里 argv[1] 存储了用户提供的目录路径。
  5. 权限设置
    mode_t mode:在调用 mkdir 时,mode 参数指定了新创建目录的权限。这里使用了 0777,意味着目录对所有用户都是可读、可写和可执行的(但实际的权限可能会受到 umask 的影响)。
#include <stdio.h>
#include "header.h"

// ~/aaaa/aaa/aa/a
int mkdirMultPath(char const *dirPath, mode_t mode)
{
    // 将文件夹路径的字符串转存到字符数组中
    char dir_path[100] = {0};
    strcpy(dir_path, dirPath);
    // 定义字符串用于临时存储截取到每一层的文件夹名称(名称后有/)
    char path[100] = {0};
    // 存储将要创建文件夹的路径(路径中有父文件夹的层级)
    char mkdir_path[100] = {0};
    // 第一次截取字符串
    char *subStr = NULL; // 存储截取到的文件夹的名称
    if (dirPath[0] == '/')
    {
        mkdir_path[0] = '/';
    }
    subStr = strtok(dir_path, "/");
    // 循环截取
    while (subStr != NULL)
    {
        // 测试代码
        // puts(subStr);
        // 将截取到的字符串转存到字符数组path中
        snprintf(path, 100, "%s/", subStr);
        // 测试代码
        // puts(path);
        // 拼接成将要创建的文件夹层级
        strcat(mkdir_path, path); //  home/yueqian/aaaa
        // 测试代码
        // puts(mkdir_path);
        // 判断将要创建的文件夹层级是否存在,如果存在就不创建(什么也不做),不存在才要创建
        if (access(mkdir_path, F_OK) == -1)
        {
            // 当前文件夹不存在,就创建
            if (mkdir(mkdir_path, mode) == -1)
            {
                perror("mkdir failed");
                return -1;
            }
        }
        // 继续截取
        subStr = strtok(NULL, "/");
    }
    return 0;
}

int main(int argc, char const *argv[])
{
    // 通过命令传入文件夹的路径
    if (argc < 2)
    {
        puts("缺少参数");
        return -1;
    }
    puts(argv[1]);

    // 调用函数创建多级文件夹
    int r = mkdirMultPath(argv[1], 0777);
    if (r == -1)
    {
        printf("%s 多级文件创建失败\n", argv[1]);
        return -1;
    }
    printf("%s 多级文件创建成功\n", argv[1]);
    return 0;
}

标签:info,文件,编程,int,常见,strcat,char,指令,文件夹
From: https://blog.csdn.net/weixin_69851948/article/details/145075716

相关文章

  • 常见文件的文件头
    JPEG文件 - FFD8FFE1 开始PNG文件 - 89504E47 开始GIF文件 -47494638 开始ZIP文件 -504B0304 或 504B0506 开始。WindowsBitmap-424DC001RAR-52617221AdobePhotoshop(psd)-38425053RichTextFormat(rtf)-7B5C727466MicrosoftOffice文件 -这些文件的文件......
  • JAVA并发编程系列 (二)
             目录1.javastart如何调用到run方法?2.synchronized关键字的底层原理,synchronize锁是如何实现的?3.notify和notifyAll区别4.synchronize锁优化锁膨胀过程?5.AQS原理6.ReentrantLock和synchronized区别7.Lock高级功能?8.简述下CAS?9.int......
  • MySQL 中常见的几种高可用架构部署方案
    MySQL中的集群部署方案前言这里来聊聊,MySQL中常用的部署方案。MySQLReplicationMySQLReplication 是官方提供的主从同步方案,用于将一个MySQL的实例同步到另一个实例中。Replication为保证数据安全做了重要的保证,是目前运用最广的MySQL容灾方案。Replication用两个......
  • 编程题-多数元素
    题目:给定一个大小为n的数组 nums,返回其中的多数元素。多数元素是指在数组中出现次数大于 ⌊n/2⌋ 的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。解法一(哈希表):使用哈希表可以快速统计出每个元素出现的次数,使用哈希映射来存储每个元素以及出现......
  • 人工智能知识分享第十二天-机器学习_常见术语整理
    机器学习常见专业术语机器学习(MachineLearning)解释:机器学习是人工智能的一个子领域,它使计算机系统能够从数据中学习并改进其性能,而无需进行明确的编程。数据集(dataset)数据集,从字面意思很容易理解,它表示一个承载数据的集合,如果说“模型”是“魔法盒”的话,那么数......
  • Dart语言的并发编程
    Dart语言的并发编程引言在现代软件开发中,处理并发操作是一项重要且复杂的任务。无论是为了提高应用程序的响应速度,还是为了有效利用系统资源,掌握并发编程都是开发者必备的技能。在众多编程语言中,Dart语言以其简洁性和高效性吸引了大量开发者的关注,尤其是在Flutter框架的推......
  • Erlang语言的并发编程
    Erlang语言的并发编程引言并发编程是现代计算机科学中一个重要的研究领域。随着多核处理器的普及,如何有效地利用这些处理器成为了开发者面临的重大挑战。Erlang语言因其独特的设计理念而在并发编程领域中脱颖而出,广泛应用于实时系统和分布式应用中。本文将详细探讨Erla......
  • JAVA2-类与对象编程(1)
    该系列分享倾向于有c语言基础的学习,想学习的指路主页c语言专栏,接下来我们正式开始面向对象编程的分享学习。软件应用:inteliIDEA2020.2一.类与对象的定义1)类是抽象的,概念性的,代表一类事物,即数据类型.2)对象是具体的,实际性的,代表一个具体事物,即实例.3)类是对象的模......
  • JavaScript 交互逻辑与异步编程
    JavaScript作为前端实现交互逻辑的核心语言,其复杂性和重要性不言而喻。在构建诸如表单验证、菜单展开收起、页面动态加载等交互功能时,我常常需要处理各种事件监听、DOM操作以及数据的动态更新。尤其是当涉及到异步操作,比如从后端接口获取数据并实时更新页面内容时,JavaScript的......
  • 【C++】穿越编程岁月,细品C++进化轨迹,深化入门基石(续章)——揭秘函数缺省参数的魅力、函
    文章目录一、函数缺省参数二、函数重载三、引用1.引用的概念和定义2.引用的特性3.引用的使用4.const引用5.指针和引用的关系四、inline内联函数和nullptr1.inline2.nullptr一、函数缺省参数   缺省参数其实就是默认参数,它是声明或定义函数时为函数的参数指定......