linux下使用c语言模拟ls [-l] [路径名] 命令
同样是一个作业,这比前一个linux下使用c语言模拟tail [-n] 命令要难一些了。
但是既然是作业,再麻烦也还是要做的,网上搜到的都是200多行甚至300多行的代码,还没有详细说明,既不想直接copy,也不想去看懂300来行几乎没注释的代码,所以就自己来了。
不借鉴别人是不可能的,于是我发现了一篇文章linux下用c实现ls命令 ,这是我找到的最简洁的一篇文章了,代码可以直接copy过来,如下:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
int main(int argc,char* argv[])
{
DIR* dir = opendir(".");
struct dirent* ent=NULL;
while((ent = readdir(dir)))
{
if((ent->d_type == 4||ent->d_type == 8)&&ent->d_name[0]!='.')
printf("%s ",ent->d_name);
}
closedir(dir);
puts("");
return 0;
}
但是显然没有达到我所要的要求,我还需要模拟ls -l 以及自定义目录呀,这篇博客里写的只能显示当前目录下所有文件或者文件夹等的名称,而且输出的排版也有点凌乱。
看完代码后,当即就发现他是使用一个定义在头文件中的结构体来实现的,那么这个结构体会不会也包含别的信息呢,比如包括了权限信息、组id、最近操作时间等等的。
我向来是不惮以最好的想法来揣测编写头文件的大佬的,然而我还不料,也不信竟让我失望到如此地步
结构体 dirent 的结构如下,从百度百科copy来的:
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长256字符 */
}
这下傻眼了,感情ls -l
和这个没搭边。然后我再去看了上面博主的文章,突然在下面发现了原来这位仁兄还写了另一篇:linux 下用 c 实现 ls -l 命令
不要急着去看,除了有目录固定,格式固定等的局限性外,还有错误。
我的代码实质上就是将他的两篇博文的功能合并了,然后允许自定义目录,也改掉了他的bug。
先说下这位博主的错误:
1、其在通过文件名获取文件细节信息时所用语句为stat( filename, &info )
,但是这个函数是判断不出一个文件是不是软链接的,而在ls -l
中,如果是软链接,显示中会在软链接的名称后加上->所指向的地址
,觉得抽象的去试一下就知道了。因而需要改成lstat( filename, &info )
,就是将stat改成lstat。
2、上述错误可以说是这位博主没有考虑到的地方,因为其并未判断文件是否为软链接,因此在mode_to_letters()
函数中,应该加上if( S_ISLNK(mode) ) str[0] = 'l';
而该文章中我一处不明白的就在:printf("%.12s ", 4 + ctime(&info_p->st_mtime));
,这条语句写了4+
不知道是为什么,但是加上和不加的时候样式有区别,如果有大佬知道望告知。
下面就放我的完整代码吧,linux虚拟机里没装中文输入法,注释用英文写了:
#include<stdio.h>
#include<unistd.h>
#include<dirent.h>
#include<sys/stat.h>
#include<string.h>
#include<pwd.h>
#include<grp.h>
int main(int argc,char **argv)
{
char c;
char *path = ".";
int isL = 0; //if have -l,set isL=1,or isL=0
while((c=getopt(argc,argv,"l"))!=-1)
{
isL = 1;
}
//printf("%d:%d",argc,optind);
if(argc > optind)
{
path = argv[optind];
}
//printf("%s",path);
struct dirent* ent = NULL;
DIR* dir = opendir(path);
int newLine = 0;
while((ent = readdir(dir)))
{
if(isL == 0)
{
if((ent->d_type == 4 || ent->d_type == 8) && ent->d_name[0]!='.')
{
printf("%-24s",ent->d_name);
newLine++;
if(newLine % 3 == 0) printf("\n");
}
}
else
{
struct stat info;
//get directory detail info
char temp[80];
strcpy(temp,path);
strcat(temp,"/");
strcat(temp,ent->d_name);
if(lstat(temp,&info) == -1)
{
printf("Cannot open the directory:%s",temp);
break;
}
else
{
if(ent->d_name[0]!='.')
{
char *uid_to_name(),*ctime(),*gid_to_name(),*filemode();
void mode_to_letters();
char modestr[11];
//transfer mode to letters
mode_to_letters(info.st_mode,modestr);
printf("%s",modestr);
printf("%4d",(int)info.st_nlink);
printf("%8s",uid_to_name(info.st_uid));
printf("%8s",gid_to_name(info.st_gid));
printf("%10ld ",(long)info.st_size);
printf("%.12s ",4+ctime(&info.st_mtime));
printf("%s",ent->d_name);
static char linkPath[1024];
readlink(temp,linkPath,1024);
if(S_ISLNK(info.st_mode))
{
printf("->%s",linkPath);
}
printf("\n");
}
}
}
}
closedir(dir);
puts("");
}
void mode_to_letters(int mode,char str[])
{
strcpy(str,"----------");
if(S_ISDIR(mode)) str[0] = 'd'; //directory
if(S_ISCHR(mode)) str[0] = 'c'; //characteristic
if(S_ISBLK(mode)) str[0] = 'b'; //block
if(S_ISLNK(mode)) str[0] = 'l'; //link
if(mode & S_IRUSR) str[1]= 'r';
if(mode & S_IWUSR) str[2]= 'w';
if(mode & S_IXUSR) str[3]= 'x';
if(mode & S_IRGRP) str[4]= 'r';
if(mode & S_IWGRP) str[5]= 'w';
if(mode & S_IXGRP) str[6]= 'x';
if(mode & S_IROTH) str[7]= 'r';
if(mode & S_IWOTH) str[8]= 'w';
if(mode & S_IXOTH) str[9]= 'x';
}
char *uid_to_name(uid_t uid)
{
struct passwd *getpwuid(),*pw_ptr;
static char numstr[10];
if((pw_ptr = getpwuid(uid)) == NULL)
{
sprintf(numstr,"%d",uid);
return numstr;
}
return pw_ptr->pw_name;
}
char *gid_to_name(gid_t gid)
{
struct group *getgrgid(),*grp_ptr;
static char numstr[10];
if((grp_ptr = getgrgid(gid)) == NULL)
{
sprintf(numstr,"%d",gid);
return numstr;
}
return grp_ptr->gr_name;
}