linux 之文件基础 (五)、文件属性

简介

当我们使用 ls -al 指令时,终端将打印文件的属性信息。那么,在我们实际编程的时候如何取获取文件的属性信息呢? 我们能不能模拟ls -al 命令,写出一个我们自己的ls -al 命令呢? 本文尝试做这个事情。其中用到了获取文件属性的API,我们的主要目的就是学习这些API。
在这里插入图片描述

1. stat等获取文件属性函数

1.1 函数原型

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

int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);

1.2 函数功能

三个函数可以获取文件属性信息。文件包括很多种,普通文件,目录等7类

  • stat函数返回一个与此命名文件有关的信息结构

1.3 三个函数区别

  • fstat函数获得已在文件描述符filedes上打开的文件的有关信息。
  • lstat函数类似于stat,但是当命名的文件是一个符号连接时,lstat返回该符号连接的有关信息,而不是由该符号连接引用的文件的信息
  • stat与之相反,返回的是链接文件引用的文件的信息

注意:

//创建一个链接文件指令
ln -s 文件名 链接名

rm 删除的时候删除的是硬链接数

1.4 函数参数

  • pathname
    文件的路径名
  • statbuf
    获取到的文件的属性存储的位置,后面详细解释。
  • fd
    已经打开的文件的描述符

1.5 statbuf参数的类型 stat 结构体

stat 结构体 用于存储获取到的文件属性,结构体的定义如下:

struct stat {
	dev_t     st_dev;     /* ID of device containing file */
	ino_t     st_ino;     /* inode number */
	mode_t    st_mode;    /* 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 */
};

下面我们介绍其中的一些字段。

1.5.1 结构体的st_ino字段

用于标识 文件的节点号。

1.5.2 结构体的 st_mode 字段

该字段用于标识文件的 类型、文件的用户权限等。
st_mode是个32位的整型变量,不过现在的linux操作系统只用了低16位(估计是鉴于以后拓展的考虑)。
在这里插入图片描述

1.5.1.1 先看File type属性区域

它位于st_mode的bit12 ~ bit15。在现代linux操作系统上文件类型共分为7种,分别是:

  • 普通文件(regular file)
  • 目录(directory)
  • 字符设备(character device)
  • 块设备(block device)
  • 管道(FIFO)
  • 符号链接文件(symbolic link)
  • 套接口文件(socket)

获取文件类型的方式:

  • 第一种方式:通过带参宏的方式。
    系统定义了多个带参数的宏用于判断st_mode,man 7 inode 可以查看。
S_ISREG(m)  is it a regular file?
S_ISDIR(m)  directory?
S_ISCHR(m)  character device?
S_ISBLK(m)  block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m)  symbolic link?  (Not in POSIX.1-1996.)
S_ISSOCK(m) socket?  (Not in POSIX.1-1996.)

带参宏的实现
#define S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)  
#define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)  
#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)  
#define S_ISCHR(m)      (((m) & S_IFMT) == S_IFCHR)  
#define S_ISBLK(m)      (((m) & S_IFMT) == S_IFBLK)  
#define S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)  
#define S_ISSOCK(m)     (((m) & S_IFMT) == S_IFSOCK)  
  • 第二种方式:通过宏的形式。
S_IFMT     0170000   bit mask for the file type bit field

S_IFSOCK   0140000   socket
S_IFLNK    0120000   symbolic link
S_IFREG    0100000   regular file
S_IFBLK    0060000   block device
S_IFDIR    0040000   directory
S_IFCHR    0020000   character device
S_IFIFO    0010000   FIFO

每一个值是一个8进制的数,st_mode 用了低16位。使用文件的st_mode 位与S_IFMT得到的结果就是下面的7种文件类型。

1.5.1.2 st_mode的再看其它位

在这里插入图片描述他们用于确定用户的权限。我们可以通过系统定义的宏来获取。

// 用户 组 其它 的权限
S_ISUID     04000   set-user-ID bit
S_ISGID     02000   set-group-ID bit (see below)
S_ISVTX     01000   sticky bit (see below)
// user	权限
S_IRWXU     00700   owner has read, write, and execute permission
S_IRUSR     00400   owner has read permission      Is read user ?  是否有读权限。
S_IWUSR     00200   owner has write permission
S_IXUSR     00100   owner has execute permission
// group 权限
S_IRWXG     00070   group has read, write, and execute permission
S_IRGRP     00040   group has read permission
S_IWGRP     00020   group has write permission
S_IXGRP     00010   group has execute permission

S_IRWXO     00007   others (not in group) have read,  write,  and execute permission
S_IROTH     00004   others have read permission
S_IWOTH     00002   others have write permission
S_IXOTH     00001   others have execute permission

1.5.4 st_uid

用于标识文件的拥有者id。当我们知道了用户id后,可以将用户id转化为用户名。因为用户id一一映射着一个用户名。获取用户名的API如下:

#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);

函数参数:

  • uid : 用户ID

函数返回值:

  • passwd 结构体指针
struct passwd {
	char   *pw_name;       /* username */
	char   *pw_passwd;     /* user password */
	uid_t   pw_uid;        /* user ID */
	gid_t   pw_gid;        /* group ID */
	char   *pw_gecos;      /* user information */
	char   *pw_dir;        /* home directory */
	char   *pw_shell;      /* shell program */
};

1.5.5 st_gid

用于标识文件所属的用户组id。当我们知道了用户组id后,可以将用户id转化为用户组名。因为用户组名id一一映射着一个用户组名。获取用户组名的API如下:

#include <sys/types.h>
#include <grp.h>
struct group *getgrgid(gid_t gid);

函数参数:

  • gid :用户组ID

函数返回值:

  • group 结构体指针
struct group {
               char   *gr_name;       /* group name */
               char   *gr_passwd;     /* group password */
               gid_t   gr_gid;        /* group ID */
               char  **gr_mem;        /* group members */
};

1.5.6 st_atime

用于存储文件的最后访问时间。

1.5.7 st_mtime

用于存储文件的最后修改时间。

2. 目录操作

2.1 打开目录

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

DIR *opendir(const char *name);
DIR *fdopendir(int fd);
  • 函数参数
    要打开的目录的目录名
  • 函数返回值
    如果name是一个合法的目录名,opendir函数返回这个目录的句柄。返回的这个句柄主要给读目录函数readdir用的。
    如果是一个非法的目录名,此函数返回NULL。

2.2 读一个目录

 #include <dirent.h>
  struct dirent *readdir(DIR *dirp);
  • 函数参数:
    需要打开的目录的指针。
  • 函数返回值:
    readdir函数需要opendir得到的句柄,每调用一次,返回当前目录中一个文件的信息。文件信息的由struct dirent结构体进行描述。
struct dirent {
	ino_t          d_ino;       /* Inode number */
	off_t          d_off;       /* Not an offset; see below */
	unsigned short d_reclen;    /* Length of this record */
	unsigned char  d_type;      /* Type of file; not supported
                                              by all filesystem types */
	char           d_name[256]; /* Null-terminated filename */
};

这个结构体我们需要关注的是最后一个字段。我们可以通过它来得到这个目录都包含有哪些文件。

2.3 改变当前进程的工作目录

#include <unistd.h>
int chdir(const char *path);
  • 函数参数:
    更改后的目录

  • 函数返回值:

3. 书写自己的 ls -al 命令

有了上述的基础后,我们就可以来书写自己的ls -al 命令了。

posted @ 2020-04-02 11:15  江南又一春  阅读(430)  评论(0编辑  收藏  举报