文件和目录

1. 文件类型

Linux下一切皆文件,但文件也分为7种类型,文件类型信息包含在struct stat结构的st_mode成员中,可以用下表中的宏来检测文件类型,这些宏的参数都是stat.st_mode。

| 文件类型 | 说 明 | 检测文件类型的宏 |
| - | - |
| 普通文件 | 最常用的文件类型,包含某种类型的数据,Linux不关心这些数据是文本还是二进制 | S_ISREG() |
| 目录文件 | 包含目录中其他文件的名字,以及与这些文件相关信息的指针 | S_ISDIR() |
| 块特殊文件 | 提供对设备带缓冲的访问,每次访问长度固定 | S_ISBLK() |
| 字符特殊文件 | 提供对设备不带缓冲的访问,每次访问长度可变 | S_ISCHR() |
| FIFO | 有名单向半双工管道,用于进程间通信 | S_ISFIFO() |
| 套接字 | socket,用于进程间网络通信 | S_ISSOCK() |
| 符号链接 | 文件本身只是个链接,指向另一个文件 | S_ISLNK() |

struct stat是记录文件信息的结构体,结构体定义如下所示,可以调用stat、fstat、lstat函数来获取文件信息,其中ls -l命令就是基于stat实现的。

struct stat
{
    dev_t     st_dev;     /* ID of device containing file */
    ino_t     st_ino;     /* inode number */
    mode_t    st_mode;    /* file type & protection */
    nlink_t   st_nlink;   /* number of hard links */
    uid_t     st_uid;     /* user ID of owner */
    gid_t     st_gid;     /* group ID of owner */
    dev_t     st_rdev;    /* device ID (if special file) */
    off_t     st_size;    /* total size, in bytes */
    blksize_t st_blksize; /* blocksize for file system I/O */
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
    time_t    st_atime;   /* time of last access */
    time_t    st_mtime;   /* time of last modification */
    time_t    st_ctime;   /* time of last status change */
};
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

//成功返回0,失败返回-1
int stat(const char *path, struct stat *buf);   //通过路径名pathname获取
int fstat(int fd, struct stat *buf);            //通过文件描述符fd获取
int lstat(const char *path, struct stat *buf);  //和stat唯一区别在于返回符号链接文件自身信息,而不是指向文件的信息

2. 文件访问权限

stat.st_mode也包含了对文件的访问权限位,每个文件有9个访问权限位,可将它们分成3类,如下表所示,其中前3行中,术语用户指的是文件所有者(owner)。

st_mode mask 含 义
S_IRUSR
S_IWUSR
S_IXUSR
user-read
user-write
user-execute
S_IRGRP
S_IWGRP
S_IXGRP
group-read
group-write
group-execute
S_IROTH
S_IWOTH
S_IXOTH
other-read
other-write
other-execute

access函数

access函数可以测试对某个文件的访问权限。

#include <unistd.h>

//成功返回0,失败返回-1
int access(const char *pathname, int mode);
  • 如果测试文件是否存在,mode就为F_OK,否则mode是由R_OK、W_OK、X_OK三个常值按位或组成的
  • R_OK、W_OK、X_OK分别测试对目标文件的读、写、可执行权限

umask函数

umask函数为进程设置文件模式创建屏蔽字,并返回之前的值,该函数没有出错返回值。

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

//返回之前的文件模式创建屏蔽字
mode_t umask(mode_t mask);
  • mask是由上面表格列出的9个st_mode mask常值按位或组成的
  • 设置完成后,由当前进程创建的新文件将关闭由mask指定的访问权限
  • umask函数只会影响当前进程

chmod函数

chmod函数可以改变已有文件的访问权限。

#include <sys/stat.h>

//参数mode即为新访问权限;成功返回0,失败返回-1
int chmod(const char *path, mode_t mode);

3. 符号链接

符号链接是对一个文件的间接指针,也被称为软链接,它与硬链接有所不同,硬链接直接指向文件的i节点,引入符号链接的原因是为了避开硬链接的一些限制。

  • 硬链接通常要求链接和文件位于同一文件系统中,而符号链接无此限制
  • 只有root用户才能创建指向目录的硬链接,而符号链接任何用户都可以创建

符号链接一般用于将一个文件或整个目录结构移到系统中另一个位置。

4. 文件与目录操作

删除文件

创建文件可以调用open函数,删除文件则需要调用unlink函数。

#include <unistd.h>

//成功返回0,失败返回-1
int unlink(const char *pathname);

该函数将pathname引用文件的链接计数减1,若链接计数达到0,则删除该文件。

创建目录

mkdir函数用于创建一个新的空目录,并自动创建.和..目录项。

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

//成功返回0,失败返回-1
int mkdir(const char *pathname, mode_t mode);

mode指定目录访问权限,通常至少需要设置一个执行权限位,以允许访问该目录中的文件名。

删除目录

rmdir函数用于删除一个空目录,空目录是只包含.和..的目录。

#include <unistd.h>

//成功返回0,失败返回-1
int rmdir(const char *pathname);

读目录

只要具有访问权限,任何用户都可以读目录,但是只有内核才能写目录,写权限位和可执行权限位只决定能否在该目录中创建和删除文件,并不代表能否写目录本身。

和读文件一样,读目录也包括三个函数:opendir、readdir、closedir。

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

DIR *opendir(const char *name);     //成功返回指针,失败返回NULL
struct dirent *readdir(DIR *dirp);  //成功返回指针,失败返回NULL
int closedir(DIR *dirp);            //成功返回0,失败返回-1

/* On Linux, the dirent structure is defined as follows: */
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; not supported by all file system types */
    char           d_name[256]; /* filename */
};

/* d_type value is defined as follows */
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 is unknown.

目录操作示例代码:递归遍历目录,打印所有文件路径

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

void display_dir(char *dir_path)
{
    DIR *dir_ptr;
    int dir_len;
    struct dirent *file_info;
    char sub_dir[256];

    if (dir_path == NULL)
    {
        return;
    }

    dir_ptr = opendir(dir_path);
    dir_len = strlen(dir_path);

    if (dir_path[dir_len - 1] == '/')
    {
        dir_path[dir_len - 1] = '\0'; //统一输出方式,避免出现dir//file.txt的现象
    }

    while (file_info = readdir(dir_ptr))
    {
        if (strcmp(file_info->d_name, ".") == 0 || strcmp(file_info->d_name, "..") == 0)
            continue;

        switch (file_info->d_type)
        {
        case DT_DIR:
            sprintf(sub_dir, "%s/%s", dir_path, file_info->d_name);
            display_dir(sub_dir);
            break;
        case DT_REG:
        case DT_BLK:
        case DT_CHR:
        case DT_FIFO:
        case DT_LNK:
            printf("%s/%s\n", dir_path, file_info->d_name);
            break;
        default:
            break;
        }
    }

    closedir(dir_ptr);
}

int main(int argc, char *argv[])
{
    display_dir(argv[1]);

    return 0;
}

posted @ 2019-09-18 23:20  原野追逐  阅读(573)  评论(0编辑  收藏  举报