简介
当我们使用 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 命令了。