stat函数,实现一个简易的ls命令(my_ls),将指定文件的属性信息打印出来(文件属性)
Stat函数
Stat、lstat、fstat这三个函数实现的功能相同,只是略微有区别,我们只要先把stat函数搞清楚了,lstat、fstat非常容易理解。
ls命令其实就是调用了这三个函数中的lstat来实现的,我们可以调用lstat函数来自己实现一个ls命令。
我们在这里说明这三个函数,并不是真的去实现一个ls命令,而是通过这三个函数的学习,深刻的理解文件各种属性,进而理解了“文件”。
stat函数
函数原型
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
我们可以通过 man 2 stat进行查看
函数功能
功能就是获取文件的属性信息。每个文件的属性信息,都是存在块设备上、该文件自己的inode节点空间中的。调用stat函数时,文件系统通过stat给的path,到块设备上索引到该文件的inode节点空间,然后将里面的文件属性信息,读到应用程序的缓存中,如此就得到了文件的属性信息。
文件属性数据中转的过程:
块设备上的inode结点 ------> 驱动程序的缓存 --------> stat函数提供的内核缓存-------->应用缓存
返回值
调用成功,返回0,失败返回-1,errno被设置。
参数说明
int stat(const char *path, struct stat buf);
(1)const char path:文件路径名
(2)struct stat *buf:应用缓存,用于存放读到的文件属性信息
缓存的类型为struct stat,通过man 2 stat,可以查看到struct stat结构体类型。
struct stat {
dev_t st_dev; /* 块设备号(ID)块设备驱动程序的编号 */
ino_t st_ino; /* inode结点号,文件属性信息所存inode节点的编号 */
mode_t st_mode; /* 文件类型和文件权限*/ ls
nlink_t st_nlink; /* 链接数 */ ls
uid_t st_uid; /* 文件所属用户ID*/ ls
gid_t st_gid; /* 文件所属组ID */ ls
dev_t st_rdev; /* 字符设备ID */
off_t st_size; /* 文件大小 */
blksize_t st_blksize; /* 系统每次按块Io操作时,块的大小(一般是512或1024) */
blkcnt_t st_blocks; /* 块的索引号 */
/* windows下,文件的时间,同样也分为这三种 */
time_t st_atime; /* 最后一次访问时间,read*/ ls
time_t st_mtime; /* 最后一次修改时间,write */
time_t st_ctime; /* 最后一次属性修改的时间,如权限被修改,文件所有者(属主)被修改 */
};
其中标记有ls的,表示这些属性,是我们ls查看时,会显示的内容。
我们可以在通过帮助手册进行查看:
实现一个自己的ls命令(my_ls),将指定文件的属性信息打印出来。
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void print_error(char *str)
{
perror(str);
exit(-1);
}
int main(void)
{
int ret = 0;
struct stat sta = {0};
//int stat(const char *pathname, struct stat *statbuf);
//获取文件属性
ret = stat("./new.txt",&sta);
if(-1 == ret) print_error("stat fail");
////打印文件属性
printf("%d %lu %d %d %ld %ld %s\n",sta.st_mode,sta.st_nlink,sta.st_uid,sta.st_gid,sta.st_size,sta.st_atime,"new.txt");
return 0;
}
执行结果为:
打印出来的都是数字
和我们ls -l new.txt所显示的显示的不一样:
Linux都是以数字形式来管理属性信息的,我们需要自己把它翻译为更好理解英文字母。
接下来我们把程序改的更像ls命令:
(1)名字改为 my_ls
(2)ls可以跟参数,让我的程序也可以跟参数
我们在编译的时候重命名最终可执行文件为 my_ls ,我们知道main函数是有参数的,所以我们在运行main函数的时候就可以传递参数。
我们对于代码进行修改:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void print_error(char *str)
{
perror(str);
exit(-1);
}
int main(int argc,char **argv)
{
int ret = 0;
struct stat sta = {0};
//int stat(const char *pathname, struct stat *statbuf);
//获取文件属性
if(argc != 2)
{
printf("my_ls fileName\n\n");
exit(-1);
}
printf("argv[0] = %s,argc[1] = %s\n",argv[0],argv[1]);
ret = stat(argv[1],&sta);
if(-1 == ret) print_error("stat fail");
////打印文件属性
printf("%d %lu %d %d %ld %ld %s\n",sta.st_mode,sta.st_nlink,sta.st_uid,sta.st_gid,sta.st_size,sta.st_atime,argv[1]);
return 0;
}
这个时候我们需要跟接一个文件名,我们在执行的时候加上文件名为参数
第一个参数是表示的是程序路径名 第二个参数表示所要显示的文件路径名
执行结果为:
如果我们没有给参数 就会直接打印文件名错误,所以我们通过给可执行程序跟上文件名参数来打印文件属性。我们程序的功能就是执行并输入所要显示的文件文件之后,显示详细文件属性信息。