【stat
案例】模拟实现 ls -l
命令
1.1 ls -l
命令
ls -l 文件名
查看某个特定文件信息
fuerer@fuerer-virtual-machine:~/Linux/lesson12$ ls -l a.txt
-rw-rw-r-- 1 fuerer fuerer 12 3月 24 15:35 a.txt
【目标】实现一个程序 app
,使用 ./app 文件名
就可以实现查看文件信息
文件信息:-rw-rw-r-- 1 fuerer fuerer 12 3月 24 15:35 a.txt
1.2 编写程序
命令行中需要传入参数,使用main函数接收
int main(int argc, char * argv[])
// argc:参数个数
// argv:参数字符串
通过stat
函数得到文件信息
// 通过stat函数获取用户传入的文件的信息
struct stat st;
int ret = stat(argv[1], &st);
// argv[1]:第二个参数为文件名称(命令为ls -l a.txt)
if(ret == -1) {
perror("stat");
return -1;
}
1.2.1 获取文件类型和操作权限
(1) 文件类型
使用 st_mode
与 掩码 S_IFMT
相与得到的结果判断
// 获取文件类型和文件权限
char perms[11] = {0}; // 用于保存文件类型和文件权限的字符串
switch(st.st_mode & S_IFMT) {
// 软链接
case S_IFLNK:
perms[0] = 'l';
break;
// 目录文件
case S_IFDIR:
perms[0] = 'd';
break;
// 普通文件
case S_IFREG:
perms[0] = '-';
break;
// 块文件
case S_IFBLK:
perms[0] = 'b';
break;
// 字符设备
case S_IFCHR:
perms[0] = 'c';
break;
// 套接字
case S_IFSOCK:
perms[0] = 's';
break;
// 管道
case S_IFIFO:
perms[0] = 'p';
break;
// 未知文件
default:
perms[0] = '?';
break;
}
(2) 文件权限
使用 st_mode
与对应的宏变量相与判断是否具有某个权限
// 文件所有者
perms[1] = (st.st_mode & S_IRUSR ? 'r' : '-');
perms[2] = (st.st_mode & S_IWUSR ? 'w' : '-');
perms[3] = (st.st_mode & S_IXUSR ? 'x' : '-');
// 文件所有组
perms[4] = (st.st_mode & S_IRGRP ? 'r' : '-');
perms[5] = (st.st_mode & S_IWGRP ? 'w' : '-');
perms[6] = (st.st_mode & S_IXGRP ? 'x' : '-');
// 其他人
perms[7] = (st.st_mode & S_IROTH ? 'r' : '-');
perms[8] = (st.st_mode & S_IWOTH ? 'w' : '-');
perms[9] = (st.st_mode & S_IXOTH ? 'x' : '-');
1.2.2 获取文件硬链接数
// 硬连接数
int linkNum = st.st_nlink;
1.2.3 获取文件所有者名称
getpwuid(uid)
返回 uid
对应的用户信息
在头文件 <pwd.h>
中
extern struct passwd *getpwuid (__uid_t __uid);
/* The passwd structure. */
struct passwd
{
char *pw_name; /* Username. */
char *pw_passwd; /* Password. */
__uid_t pw_uid; /* User ID. */
__gid_t pw_gid; /* Group ID. */
char *pw_gecos; /* Real name. */
char *pw_dir; /* Home directory. */
char *pw_shell; /* Shell program. */
};
通过passwd
结构体中的pw_name
获取用户名称
// 文件所有者
char * fileUser = getpwuid(st.st_uid)->pw_name;
1.2.4 获取所在组名称
getgrgid(gid)
返回 gid
对应的组的信息
在头文件 <grp.h>
中
extern struct group *getgrgid (__gid_t __gid);
获取组的名称
// 文件所在组
char * fileGrp = getgrgid(st.st_gid)->gr_name;
1.2.5 获取文件大小
st_size
的类型为 size_t
是 long int
类型
// 文件大小
long int fileSize = st.st_size;
1.2.6 获取文件修改时间
st.st_mtim
是 long int
类型,表示从1970年0时0分0秒到目前代表时间的毫秒数
ctime()
函数将毫秒数转换为当地的时间
在<time.h>
头文件中
extern char *ctime (const time_t *__timer) __THROW;
传递地址
// 获取修改的时间 从1970年到目前的毫秒数
char * time = ctime(&st.st_mtime);
转换后自动带了回车换行
1.2.7 信息拼接输出
sprintf
函数
/* Write formatted output to S. */
extern int sprintf (char *__restrict __s,const char *__restrict __format, ...) __THROWNL;
// 输出
char buf[1024];
// 将变量拼接为字符串 放入buf中
sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum,fileUser, fileGrp, fileSize, time, argv[1]);
printf("%s\n", buf);
ls-l.c
文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
//模拟实现 ls -l 命令
//-rw-rw-r-- 1 fuerer fuerer 12 3月 24 15:35 a.txt
int main(int argc, char * argv[]) {
// 命令要传参数
// argc:参数个数
// argv:命令字符串
// 判断参数是否正确
if(argc < 2) {
printf("%s filename\n", argv[0]);
// 第一个参数即为命令名称
return -1;
}
// 通过stat函数获取用户传入的文件的信息
struct stat st;
int ret = stat(argv[1], &st);
if(ret == -1) {
perror("stat");
return -1;
}
// 获取文件类型和文件权限
char perms[11] = {0}; // 用于保存文件类型和文件权限的字符串
switch(st.st_mode & S_IFMT) {
// 软链接
case S_IFLNK:
perms[0] = 'l';
break;
// 目录文件
case S_IFDIR:
perms[0] = 'd';
break;
// 普通文件
case S_IFREG:
perms[0] = '-';
break;
// 块文件
case S_IFBLK:
perms[0] = 'b';
break;
// 字符设备
case S_IFCHR:
perms[0] = 'c';
break;
// 套接字
case S_IFSOCK:
perms[0] = 's';
break;
// 管道
case S_IFIFO:
perms[0] = 'p';
break;
// 未知文件
default:
perms[0] = '?';
break;
}
// 判断文件的访问权限
// 文件所有者
perms[1] = (st.st_mode & S_IRUSR ? 'r' : '-');
perms[2] = (st.st_mode & S_IWUSR ? 'w' : '-');
perms[3] = (st.st_mode & S_IXUSR ? 'x' : '-');
// 文件所有组
perms[4] = (st.st_mode & S_IRGRP ? 'r' : '-');
perms[5] = (st.st_mode & S_IWGRP ? 'w' : '-');
perms[6] = (st.st_mode & S_IXGRP ? 'x' : '-');
// 其他人
perms[7] = (st.st_mode & S_IROTH ? 'r' : '-');
perms[8] = (st.st_mode & S_IWOTH ? 'w' : '-');
perms[9] = (st.st_mode & S_IXOTH ? 'x' : '-');
// 硬连接数
int linkNum = st.st_nlink;
// 文件所有者
char * fileUser = getpwuid(st.st_uid)->pw_name;
// 文件所在组
char * fileGrp = getgrgid(st.st_gid)->gr_name;
// 文件大小
long int fileSize = st.st_size;
// 获取修改的时间 从1970年到目前的毫秒数
char * time = ctime(&st.st_mtime);
// 去除回车换行
char mtime[512] = {0};
strncpy(mtime, time, strlen(time) - 1);
// 输出
char buf[1024];
// 将变量拼接为字符串 放入buf中
sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);
printf("%s\n", buf);
return 0;
}
1.3 编译运行
fuerer@fuerer-virtual-machine:~/Linux/lesson12$ gcc ls-l.c -o ls
fuerer@fuerer-virtual-machine:~/Linux/lesson12$ ls
a.txt b.txt ls ls-l.c stat stat.c
fuerer@fuerer-virtual-machine:~/Linux/lesson12$ ./ls
./ls filename
fuerer@fuerer-virtual-machine:~/Linux/lesson12$ ./ls a.txt
-rw-rw-r-- 1 fuerer fuerer 12 Thu Mar 24 15:35:59 2022
a.txt
注意:ls
为shell命令,使用 ./ls
才是执行生成的ls
可执行文件
生成的时间自动带有回车换行
strncpy
实现字符串的拷贝strlen
返回字符串长度
// 去除回车换行
char mtime[512] = {0};
strncpy(mtime, time, strlen(time) - 1);
结果对比
fuerer@fuerer-virtual-machine:~/Linux/lesson12$ ./ls a.txt
-rw-rw-r-- 1 fuerer fuerer 12 Thu Mar 24 15:35:59 2022 a.txt
fuerer@fuerer-virtual-machine:~/Linux/lesson12$ ls -l a.txt
-rw-rw-r-- 1 fuerer fuerer 12 3月 24 15:35 a.txt