关键函数的功能及说明
1.void display_attribute(struct stat buf,char *name)
函数功能:打印文件名为name的文件信息,如
含义分别为:文件的类型和访问权限,文件的链接数,文件的所有者,文件所有者所属的组,文件大小,文件创建的时间
2.void dispaly_single(char *name)
函数功能:输出文件的文件名,如果命令中没有-l选项,则输出文件名时要保证上下对齐,如:
3.void display(int flag,char *pathname)
函数功能:根据命令行参数(存放在flag中)和完整路径名(存放在pathname中)显示目标文件,参数flag可以取以下值或者它们的组合
4.void diaplay_dir(int flag_param,char *path)
函数功能:为显示某个目录下的文件做准备,参数flag_param用于在调用display函数时作为其参数flag的实参,path是要显示的目录
函数流程
准备工作代码:
#include<cstdlib>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
#include<dirent.h>
#include<linux/limits.h>
#include<pwd.h>
#include<grp.h>
#include<algorithm>
using namespace std;
#define PARAM_NONE 0//无参数
#define PARAM_A 1//-a 显示所有文件
#define PARAM_L 2 //-l 一行只显示一个文件的详细信息
#define MAXROWLEN 80 //一行显示的最多字符数
int g_leave_len=MAXROWLEN;//一行剩余长度,用于输出对齐
int g_maxlen; //存放某目录下最长文件名的长度
//自定义错误处理函数
void my_err(const char *err_string,int line){
fprintf(stderr,"line:%d ",line);
perror(err_string);
exit(1);
}
int cmp(const void *a, const void *b)
{
return ((char *)a)[0] - ((char *)b)[0];
}
顺便了解
pwd.h
和grp.h
是两个C语言标准库头文件,用于处理用户和组的信息,通常用于UNIX和类UNIX系统。
该头文件定义了与用户账户信息相关的结构和函数。主要包括获取和处理用户账户信息的功能,例如用户名、用户ID(UID)、组ID(GID)、用户家目录等。一些常见的函数和结构体包括:
pwd.h
- Password Database:
getpwnam
:通过用户名获取用户信息。getpwuid
:通过用户ID获取用户信息。struct passwd
:用于存储用户账户信息的结构体。该头文件定义了与用户组信息相关的结构和函数。主要包括获取和处理用户组信息的功能,例如组名、组ID(GID)、组成员等。一些常见的函数和结构体包括:
grp.h
- Group Database:
struct group
:用于存储用户组信息的结构体。getgrgid
:通过组ID获取组信息。getgrnam
:通过组名获取组信息。
如果你不清楚函数代码实在干嘛,可以看一下上面的运行结果,这样才知道到底要输出什么
display_attribute代码:
//获取文件属性并打印
void display_attribute(struct stat buf,char *name){
char buf_time[32];
struct passwd *psd;//从该结构体中获取文件所有者的用户名
struct group *grp;//从该结构体中获取文件所有者所属组的组名
//获取并打印文件类型
if(S_ISLNK(buf.st_mode))
printf("l");//符号链接
else if(S_ISREG(buf.st_mode))
printf("-");//普通文件
else if(S_ISDIR(buf.st_mode))
printf("d");//目录
else if(S_ISCHR(buf.st_mode))
printf("c");//字符设备文件
else if(S_ISBLK(buf.st_mode))
printf("b");//块设配文件
else if(S_ISSOCK(buf.st_mode))
printf("s");//套接字文件
else if(S_ISFIFO(buf.st_mode))
printf("f");//FIFO文件
//获取并打印文件所有者的权限
if(buf.st_mode&S_IRUSR)
printf("r");
else printf("-");
if(buf.st_mode&S_IWUSR)
printf("w");
else printf("-");
if(buf.st_mode&S_IXUSR)
printf("x");
else printf("-");
//获取并打印与文件所有者同组的用户对该文件的操作权限
if(buf.st_mode&S_IRGRP)
printf("r");
else printf("-");
if(buf.st_mode&S_IWGRP)
printf("w");
else printf("-");
if(buf.st_mode&S_IXGRP)
printf("x");
else printf("-");
//获取并打印其他用户对该文件的操作权限
if(buf.st_mode&S_IROTH)
printf("r");
else printf("-");
if(buf.st_mode&S_IWOTH)
printf("w");
else printf("-");
if(buf.st_mode&S_IXOTH)
printf("x");
else printf("-");
printf(" ");
//根据uid与gid获取文件所有者的用户名与组名
psd=getpwuid(buf.st_uid);
grp=getgrgid(buf.st_gid);
printf("%4d",buf.st_nlink);//打印文件的链接数
printf("%-8s",psd->pw_name);
printf("%-8s",grp->gr_name);
printf("%6d",buf.st_size);//打印文件的大小
strcpy(buf_time,ctime(&buf.st_mtime));
buf_time[strlen(buf_time)-1]='\0';//去掉换行符
printf(" %s",buf_time);//打印文件的时间信息
}
struct stat这个结构体在《Linux C编程实战》笔记:文件属性操作函数-CSDN博客 这里放过具体的信息,可以看到主要是从st_mode获取信息
S_ISLNK
是一个宏(macro),通常用于检查给定文件的模式是否表示一个符号链接(Symbolic Link)。这个宏在UNIX和类UNIX系统的系统编程中很常见,特别是在处理文件和目录时。
S_ISLNK
的定义通常在<sys/stat.h>
头文件中,这个头文件包含了有关文件状态(file status)的相关宏和结构体定义。它的典型用法是与st_mode
结构成员一起使用
其他的宏都是类似的作用,用来检查文件具体是那种类型的。
buf.st_mode & S_IRGRP
是一个位运算的操作,用于检查给定文件的权限位
S_IRGRP之类的文件权限的宏在 《Linux C编程实战》笔记:文件读写-CSDN博客 有具体解释过,通过这个操作可以得知文件的具体权限
用
getpwuid
和getgrgid
函数来获取文件或目录的所有者(user)和所属组(group)的相关信息。这两个函数通常与文件状态结构struct stat
中的st_uid
(用户ID)和st_gid
(组ID)成员一起使用。
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
#include <grp.h>
struct group *getgrgid(gid_t gid);
struct passwd {
char *pw_name; // 用户名
char *pw_passwd; // 加密后的密码
uid_t pw_uid; // 用户ID
gid_t pw_gid; // 主组ID
char *pw_gecos; // 用户的真实姓名等描述信息
char *pw_dir; // 用户的主目录
char *pw_shell; // 用户的登录shell
};
struct group {
char *gr_name; // 组名
char *gr_passwd; // 加密后的密码(在 /etc/gshadow 中)
gid_t gr_gid; // 组ID
char **gr_mem; // 用户名的数组,表示属于这个组的成员
};
ctime的具体用法也在 《Linux C编程实战》笔记:文件属性操作函数-CSDN博客 有提到
%-8s
的格式时,它包含两个部分:-8
是一个标志(flag),表示左对齐输出,而%s
表示将一个字符串插入到该位置。
%s
:插入一个字符串。-8
:左对齐,并占用最少8个字符的宽度。如果字符串长度不足8个字符,将用空格在右侧填充。
dispaly_single代码:
//在没有使用-l选项时,打印一个文件名,打印时上下对齐
void display_single(char *name){
int i,len;
//如果本行不足以打印一个文件名则换行
if(g_leave_len<g_maxlen){
printf("\n");
g_leave_len=MAXROWLEN;
}
len=strlen(name);
len=g_maxlen-len;
printf("%-s",name);
//为了保持对齐,需要填充空格来实现和最长文件名占位相同
for(i=0;i<len;i++){
printf(" ");
}
printf(" ");
g_leave_len-=(g_maxlen+2);//改行剩下的长度减去最长文件名加两个空格的长度
}
这里主要是格式控制,在准备代码里有每个变量具体的含义,每次输出主要是更新g_leave_len
display代码:
/*
* 根据命令行参数和完整路径名显示目标文件
* 参数flag:命令行参数
* 参数pathname:包含了文件名的路径名
*/
void display(int flag,char *pathname){
int i,j;
struct stat buf;
char name[NAME_MAX+1];
//从路径中解析出文件名,其实就是最后一个/后面的字符串了
//书中代码是这么来获得的,感觉应该有更好的方法(C语言好像没有split函数,多少会麻烦一些)
for(i=0,j=0;i<strlen(pathname);i++){
if(pathname[i]=='/'){
j=0;
continue;
}
name[j++]=pathname[i];
}
name[j]='\0';
//用lstat而不是stat以方便解析链接文件
if(lstat(pathname,&buf)==-1)
my_err("stat",__LINE__);
switch (flag)
{
case PARAM_NONE://没有选项
if(name[0]!='.'){//隐藏的.目录文件就忽略
display_single(name);
}
break;
case PARAM_A:
display_single(name);
break;
case PARAM_L:
if(name[0]!='.'){
display_attribute(buf,name);//详细信息
printf(" %s-\n",name);//最后打印文件名
}
break;
case PARAM_A+PARAM_L:
display_attribute(buf,name);
printf(" %-s\n",name);
break;
default:
break;
}
}
NAME_MAX在《Linux C编程实战》笔记:目录操作-CSDN博客
lstat在《Linux C编程实战》笔记:文件属性操作函数-CSDN博客
display_dir代码:
void display_dir(int flag_param,char *path){
DIR *dir;
struct dirent *ptr;
int count=0;
char filenames[256][PATH_MAX+1],temp[PATH_MAX+1];
//获取该目录下文件总数和最长的文件名
dir=opendir(path);
if(dir==nullptr)
my_err("opendir",__LINE__);
while((ptr=readdir(dir))!=nullptr){
if(g_maxlen<strlen(ptr->d_name))
g_maxlen=strlen(ptr->d_name);//更新最长的文件名长度
count++;
}
closedir(dir);
if (count>256)
{
my_err("too maney files under this dir",__LINE__);
}
int i,j,len=strlen(path);
//获取该目录下所有的文件名
dir=opendir(path);
for(i=0;i<count;i++){
ptr=readdir(dir);
if(ptr==nullptr)
my_err("readdir",__LINE__);
strncpy(filenames[i],path,len);
filenames[i][len]='\0';
strcat(filenames[i],ptr->d_name);//把目录名称和目录下的文件名拼一起
filenames[i][len+strlen(ptr->d_name)]='\0';
}
//给文件名排序
//源代码里用的是冒泡,排序不是重点,用一下stl算了,偷个懒
qsort(filenames,count,sizeof(filenames[0]),cmp);
for(i=0;i<count;i++)
display(flag_param,filenames[i]);
closedir(dir);
//如果命令行中没有-l选项,打印一个换行符
if((flag_param&PARAM_L)==0)
printf("\n");
}
打开目录和遍历目录的操作我已经演示过了了《Linux C编程实战》笔记:目录操作-CSDN博客
main函数代码:
int main(int argc,char **argv){
int i,j,k,num;
char path[PATH_MAX+1];
char param[32];//保存命令行参数,目标文件名和目录名不在这里
int flag_param=PARAM_NONE;//参数种类
struct stat buf;
//命令行参数的解析,分析 -l、-a、-al、-la选项
j=0;
num=0;
for(i=1;i<argc;i++){
if(argv[i][0]=='-'){//是一个命令
for(k=1;k<strlen(argv[i]);k++,j++){
param[j]=argv[i][k];//把命令的参数(a或l)都放到param数组里
}
num++;//保存"-"的个数
}
}
//j现在是param数组的长度
for(int i=0;i<j;i++){//遍历param数组,看参数都是什么
if(param[i]=='a'){
flag_param|=PARAM_A;
continue;
}
else if(param[i]=='l'){
flag_param|=PARAM_L;
continue;
}else{//a,l以外的参数,不支持
printf("my_ls:invalid option -%c\n",param[i]);
exit(1);
}
}
param[j]='\0';
//如果没有输入文件名或目录则显示当前目录
if((num+1)==argc){//这表示除了参数,没有输入文件或目录
strcpy(path,"./");//path变成当前目录
path[2]='\0';
display_dir(flag_param,path);//用显示目录的函数
return 0;
}
//开始遍历文件或目录
i=1;
do
{
//如果不是目标文件或目录,解析下一个命令行参数
if(argv[i][0]=='-'){
i++;
continue;
}else{
strcpy(path,argv[i]);
//如果目标文件或目录不存在,报错并退出
if(stat(path,&buf)==-1)
my_err("stat",__LINE__);
if(S_ISDIR(buf.st_mode)){//argv[i]是一个目录
//如果目录的最后一格字符不是'/',就把'/'加上
if(path[strlen(argv[i])-1]!='/'){
path[strlen(argv[i])]='/';
path[strlen(argv[i])+1]='\0';
}
else path[strlen(argv[i])]='\0';
//也是调用显示目录的函数
display_dir(flag_param,path);
i++;
}
else{//argv[i]是一个文件
display(flag_param,path);
i++;
}
}
} while (i<argc);
return 0;
}
这里说一下argc和argv,不然可能搞不懂。
运行程序时,如果啥都不跟,直接./a.out这种的话,argc默认为1,argv[0]是程序名字,如果后面带了参数,比如这样
./a.out arg1 arg2 arg3
那argc就是4,argv[1],argv[2],argv[3]存的是参数的字符串
所以argv代码里都是从1开始,因为argv[0]默认存的是程序名称。
编译运行一下
结果还是很完美的
一整个源码也在这,方便直接抄了,但是没有注释
#include<cstdlib>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
#include<dirent.h>
#include<linux/limits.h>
#include<pwd.h>
#include<grp.h>
#include<algorithm>
using namespace std;
#define PARAM_NONE 0
#define PARAM_A 1
#define PARAM_L 2
#define MAXROWLEN 80
int g_leave_len=MAXROWLEN;
int g_maxlen;
//自定义错误处理函数
void my_err(const char *err_string,int line){
fprintf(stderr,"line:%d ",line);
perror(err_string);
exit(1);
}
int cmp(const void *a, const void *b)
{
return ((char *)a)[0] - ((char *)b)[0];
}
void display_attribute(struct stat buf,char *name){
char buf_time[32];
struct passwd *psd;
struct group *grp;
if(S_ISLNK(buf.st_mode))
printf("l");
else if(S_ISREG(buf.st_mode))
printf("-");
else if(S_ISDIR(buf.st_mode))
printf("d");
else if(S_ISCHR(buf.st_mode))
printf("c");
else if(S_ISBLK(buf.st_mode))
printf("b");
else if(S_ISSOCK(buf.st_mode))
printf("s");
else if(S_ISFIFO(buf.st_mode))
printf("f");
if(buf.st_mode&S_IRUSR)
printf("r");
else printf("-");
if(buf.st_mode&S_IWUSR)
printf("w");
else printf("-");
if(buf.st_mode&S_IXUSR)
printf("x");
else printf("-");
if(buf.st_mode&S_IRGRP)
printf("r");
else printf("-");
if(buf.st_mode&S_IWGRP)
printf("w");
else printf("-");
if(buf.st_mode&S_IXGRP)
printf("x");
else printf("-");
if(buf.st_mode&S_IROTH)
printf("r");
else printf("-");
if(buf.st_mode&S_IWOTH)
printf("w");
else printf("-");
if(buf.st_mode&S_IXOTH)
printf("x");
else printf("-");
printf(" ");
psd=getpwuid(buf.st_uid);
grp=getgrgid(buf.st_gid);
printf("%4d",buf.st_nlink);
printf("%-8s",psd->pw_name);
printf("%-8s",grp->gr_name);
printf("%6d",buf.st_size);
strcpy(buf_time,ctime(&buf.st_mtime));
buf_time[strlen(buf_time)-1]='\0';
printf(" %s",buf_time);
}
void display_single(char *name){
int i,len;
if(g_leave_len<g_maxlen){
printf("\n");
g_leave_len=MAXROWLEN;
}
len=strlen(name);
len=g_maxlen-len;
printf("%-s",name);
for(i=0;i<len;i++){
printf(" ");
}
printf(" ");
g_leave_len-=(g_maxlen+2);
}
void display(int flag,char *pathname){
int i,j;
struct stat buf;
char name[NAME_MAX+1];
for(i=0,j=0;i<strlen(pathname);i++){
if(pathname[i]=='/'){
j=0;
continue;
}
name[j++]=pathname[i];
}
name[j]='\0';
if(lstat(pathname,&buf)==-1)
my_err("stat",__LINE__);
switch (flag)
{
case PARAM_NONE:
if(name[0]!='.'){
display_single(name);
}
break;
case PARAM_A:
display_single(name);
break;
case PARAM_L:
if(name[0]!='.'){
display_attribute(buf,name);
printf(" %s-\n",name);
}
break;
case PARAM_A+PARAM_L:
display_attribute(buf,name);
printf(" %-s\n",name);
break;
default:
break;
}
}
void display_dir(int flag_param,char *path){
DIR *dir;
struct dirent *ptr;
int count=0;
char filenames[256][PATH_MAX+1],temp[PATH_MAX+1];
dir=opendir(path);
if(dir==nullptr)
my_err("opendir",__LINE__);
while((ptr=readdir(dir))!=nullptr){
if(g_maxlen<strlen(ptr->d_name))
g_maxlen=strlen(ptr->d_name);
count++;
}
closedir(dir);
if (count>256)
{
my_err("too maney files under this dir",__LINE__);
}
int i,j,len=strlen(path);
dir=opendir(path);
for(i=0;i<count;i++){
ptr=readdir(dir);
if(ptr==nullptr)
my_err("readdir",__LINE__);
strncpy(filenames[i],path,len);
filenames[i][len]='\0';
strcat(filenames[i],ptr->d_name);
filenames[i][len+strlen(ptr->d_name)]='\0';
}
qsort(filenames,count,sizeof(filenames[0]),cmp);
for(i=0;i<count;i++)
display(flag_param,filenames[i]);
closedir(dir);
if((flag_param&PARAM_L)==0)
printf("\n");
}
int main(int argc,char **argv){
int i,j,k,num;
char path[PATH_MAX+1];
char param[32];
int flag_param=PARAM_NONE;
struct stat buf;
j=0;
num=0;
for(i=1;i<argc;i++){
if(argv[i][0]=='-'){
for(k=1;k<strlen(argv[i]);k++,j++){
param[j]=argv[i][k];
}
num++;
}
}
for(int i=0;i<j;i++){
if(param[i]=='a'){
flag_param|=PARAM_A;
continue;
}
else if(param[i]=='l'){
flag_param|=PARAM_L;
continue;
}else{
printf("my_ls:invalid option -%c\n",param[i]);
exit(1);
}
}
param[j]='\0';
if((num+1)==argc){
strcpy(path,"./");
path[2]='\0';
display_dir(flag_param,path);
return 0;
}
i=1;
do
{
if(argv[i][0]=='-'){
i++;
continue;
}else{
strcpy(path,argv[i]);
if(stat(path,&buf)==-1)
my_err("stat",__LINE__);
if(S_ISDIR(buf.st_mode)){
if(path[strlen(argv[i])-1]!='/'){
path[strlen(argv[i])]='/';
path[strlen(argv[i])+1]='\0';
}
else path[strlen(argv[i])]='\0';
display_dir(flag_param,path);
i++;
}
else{
display(flag_param,path);
i++;
}
}
} while (i<argc);
return 0;
}