C语言用实现Shell指令ls -l
要求:利用C语言实现Shell指令ls -l这里我们只在ubuntu的终端里面输入ls -l指令,看看得到结果。在这里我们可以看到终端将当前文件夹内的文件按以下格式显示:文件类型文件权限目录(链接个数)所属用户所属用户组文件大小修改时间文件名称drwxrwxr-x3linuxlinux...
要求:利用C语言实现Shell指令ls -l
这里我们只在ubuntu的终端里面输入ls -l指令,看看得到结果。
在这里我们可以看到终端将当前文件夹内的文件按以下格式显示:
文件类型 | 文件权限 | 目录(链接个数) | 所属用户 | 所属用户组 | 文件大小 | 修改时间 | 文件名称 |
d | rwxrwxr-x | 3 | linux | linux | 4096 | 2月 3 18:29 | A |
在linux中Shell指令ls -l指令的作用是:以列表的形式显示指定文件内文件的详细信息,无参数则默认显示当前目录。
所以我们在使用C语言实现ls -l指令的时候,需要按照下面步骤:
第一步:利用opendir()函数去打开某个目录。
第二步:利用readdir()函数去读取该目录下的所有文件信息。
第三步:利用stat()函数,读取文件信息,包括其文件类型,文件权限,文件链接数,文件大小,文件修改时间(UTC)等等
第四步:利用getpwuid()函数,获取文件所属用户名,利用getgrgid()函数,获取文件所属用户组名
第五步:利用locatime()函数,将文件修改时间由UTC转换为所属地区时间
这里我们需要注意的是,readdir()函数每次只会读取到一个文件名,所以每次进行进行第二步读取文件名信息,执行完后面的三四五步后,程序应该重新跳到第二步继续读取下一个文件名,直到readdir()函数读取不到文件名为止。
分步实现:
这里面涉及的函数较多,及结构体变量较多,建议在终端打开多个窗口用于查询函数和结构体变量的介绍。
第一步:打开目录,需要使用opendir()函数,函数介绍如下
函数原型 | 函数作用 |
DIR *opendir(const char *name); | 打开一个目录流,并返回指向目录流的指针,打开失败返回NULL |
name:目录名(路径) |
第二步:读取该目录下的文件信息,需要使用readdir()函数,函数介绍如下
函数原型 | 函数作用 |
struct dirent * readdir(DIR * dir); | 返回一个指向当前目录流的指针结构体,当到达目录流或的末尾时或者发生错误,它返回NULL |
dir:指向目录流的指针 |
指针结构体为:
struct dirent{
ino_t d_ino; // inode number
off_t d_off; // not an offset; see NOTES
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]; // filename 此项即为我们需要的文件名
}
这里我们需要注意的是,每次调用该函数会按序得到一个文件的信息,同时内部指针会自动移位,再次调用的时候,会得到下一个文件的信息。所以在程序中不断调用该函数就可以得到查看目录下的所以文件信息。
并且再之前分析上,我们第二步是需要不断循环的,所以在程序中可以这样做:
//p_dir为指向目录的指针
//d_dir为指向目录流的结构体指针
while(d_dir = readdir(p_dir)) //不断调用能按序得到目录下的所有文件信息,当返回值为NULL时,循环打断
{
printf("filename is %s \n", d_dir->d_name); //打印文件名
//第三步
//第四步
//第五步
}
第三、第四、第五步没有顺序之分,这里我们按照--> ls -l命令显示格式来按序实现:
首先利用stat()函数,得到文件的具体信息:
函数原型 | 函数作用 |
int stat(const char *path, struct stat *buf); | 通过路径path获取文件信息,并保存在buf所指的结构体stat中 |
path:路径 buf:文件信息保存区 |
struct 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 文件所属用户ID
gid_t st_gid; // group ID of owner 文件所属组ID
dev_t st_rdev; // device ID (if special file)
off_t st_size; // total size, in bytes 文件大小
blksize_t st_blksize; // blocksize for filesystem 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
};
这里我们先利用st_mode获取文件的类型:
在man帮助手册中,我们可以查到关于st_mode的介绍,
The following flags are defined for the st_mode field:
name | octal | description |
S_IFMT | 0170000 | bit mask for the file type bit fields |
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 |
所以关于文件类型就可以这样区分:
switch (st.st_mode & S_IFMT)
{
case S_IFBLK: printf("b"); break;//块设备类型
case S_IFCHR: printf("c"); break;//字符类型
case S_IFDIR: printf("d"); break;//目录类型
case S_IFIFO: printf("p"); break;//管道类型
case S_IFLNK: printf("l"); break;//链接类型
case S_IFREG: printf("-"); break;//文件类型
case S_IFSOCK: printf("s"); break;//套接字类型
default: printf("?"); break;//无法识别类型
}
下来我们来实现判断文件的权限,文件的权限信息同样保存在st_mode当中:
name | octal | description |
S_IRUSR | 00400 | owner has read permission |
S_IWUSR | 00200 | owner has write permission |
S_IXUSR | 00100 | owner has execute permission |
S_IRGRP | 00040 | group has read permission |
S_IWGRP | 00020 | group has write permission |
S_IXGRP | 00010 | group has execute permission |
S_IROTH | 00004 | others have read permission |
S_IWOTH | 00002 | others have write permission |
S_IXOTH | 00001 | others have execute permission |
文件权限显示格式:【文件或文件夹】【owner权限】【group权限】【others权限】
在这里我们使用一种洁简的方式显示,文件权限的显示:
for (i = 8; i >= 0; i--)
{
if (st.st_mode & (1 << i))
{
switch( i % 3)
{
case 2: printf("r"); break; //文件该处具有读权限
case 1: printf("w"); break; //文件该处具有写权限
case 0: printf("x"); break; //文件该处具有执行权限
}
}
else
{
printf("-"); //文件该处无任何权限
}
}
下面获取文件的链接数,该项就是struct stat中的st_nlink变量。我们直接读取其值即可。
printf("file links is %d \n", st.st_nlink);
下面获取文件所属用户和获取文件所属用户组,由于在struct stat变量中的st_uid和st_gid只是文件所属用户和文件所属用户组的ID,并不是他们的名字,所以我们需要借助两个函数得到其名字,分别是getpwuid()和getgrgid()函数。
函数介绍如下:
函数原型 | 函数作用 |
struct passwd *getpwuid(uid_t uid); | 通过用户的uid得到用户的数据,以passwd结构返回 |
uid:用户的uid(识别码) | |
struct group *getgrgid(gid_t gid); | 通过组的gid得到组的数据,以group结构返回 |
gid:组的gid(识别码) |
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
};
group结构如下:
struct group
{
char *gr_name; // group name 该处即为组名
char *gr_passwd; // group password
gid_t gr_gid; // group ID /
char **gr_mem; // group members
};
获取文件所属用户名和文件所属组名程序如下所示:
struct passwd *pw;
struct group *gp;
pw = getpwuid(st.st_uid);
gp = getgruid(st.st_gid);
printf("owner name is %s \n", pw->pw_name);
printf("group name is %s \n", gp->gr_name);
下面获取文件的大小,该项就是struct stat中的st_size变量。我们直接读取其值即可。
printd("file size is %d \n", st.st_size);
最后是获取文件最后修改时间,这里我们需要利用localtime()函数将UTC时间转换成所属时区的时间信息。
struct tm *l_time;
l_time = localtime(&st.st_mtime);
printf("%2d月 %2d %2d:%2d ", l_time->tm_mon,
l_time->tm_mday,
l_time->tm_hour,
l_time->tm_min);
到这里就已经用C语言将ls -l指令功能实现了。下面我们看看具体程序的效果:
图中上半部分是C语言代码实现的功能,下半部分是ls -l命令的结果。除了顺序不一样以外,结果基本一致。
完整代码如下:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
//C语言实现ll指令功能
int main(int argc, const char *argv[])
{
int i = 0;
DIR *p_dir = NULL;
struct dirent *d_dir = NULL;
struct tm *l_time = NULL;
struct passwd *pw = NULL;
struct group *gp = NULL;
struct stat st;
memset(&st, 0, sizeof(st));
if (NULL == (p_dir = opendir(argv[1]))) //打开目录
{
perror("opendir");
}
while(d_dir = readdir(p_dir)) //循环读取目录内的文件
{
if ('.' == *(d_dir->d_name)) //去除隐藏文件
continue;
stat(d_dir->d_name, &st); //获取文件详细信息
switch (st.st_mode & S_IFMT) //判断文件类型
{
case S_IFBLK: printf("b"); break; //块设备类型
case S_IFCHR: printf("c"); break; //字符类型
case S_IFDIR: printf("d"); break; //目录类型
case S_IFIFO: printf("p"); break; //管道类型
case S_IFLNK: printf("l"); break; //链接类型
case S_IFREG: printf("-"); break; //文件类型
case S_IFSOCK: printf("s"); break; //套接字类型
default: printf("?"); break; //无法识别类型
}
for (i = 8; i >= 0; i--) //判断文件权限
{
if (st.st_mode & (1 << i))
{
switch( i % 3)
{
case 2: printf("r"); break; //文件该处具有读权限
case 1: printf("w"); break; //文件该处具有写权限
case 0: printf("x"); break; //文件该处具有执行权限
}
}
else
{
printf("-"); //文件该处无任何权限
}
}
printf(" %d ", st.st_nlink); //文件链接数
pw = getpwuid(st.st_uid);
gp = getgrgid(st.st_gid);
printf("%s ", pw->pw_name); //文件所属用户名
printf("%s ", gp->gr_name); //文件所属组组名
printf("%6d ", st.st_size); //文件大小
l_time = localtime(&st.st_mtime); //文件最后修改时间由UTC时间转换为地区时间
printf("%2d月 %2d %2d:%2d ", l_time->tm_mon + 1 ,
l_time->tm_mday,
l_time->tm_hour,
l_time->tm_min);
printf("%s \n", d_dir->d_name); //文件名
}
return 0;
}
仓促成文,不当之处,尚祈方家和读者批评指正。联系邮箱1772348223@qq.com
更多推荐
所有评论(0)