LinuxUNIX编程:使用C语言实现ls命令

LinuxUNIX编程:使用C语言实现ls命令,第1张

概述刚好把 Linux/UNIX 编程中的文件和IO部分学完了,就想编写个 ls 命令练习一下,本以为很简单,调用个 stat 就完事了,没想到前前后后弄了七八个小时,90%的时间都用在格式化(像 ls

刚好把 linux/UNIX 编程中的文件和IO部分学完了,就想编写个 ls 命令练习一下,本以为很简单,调用个 stat 就完事了,没想到前前后后弄了七八个小时,90%的时间都用在格式化(像 ls -l 中的对齐)输出了,反反复复改了好几遍。

一共实现了常用的四个选项:-a -h -l -d。可以从命令行参数中同时接受多个目录和文件,然后分开输出。

演示:

-a 命令:

-l 和 -h 命令:

 

-d 命令:

参数同时接受多个文件和目录名:

 

思路:

先使用 getopt 解析选项

然后判断参数有没有目录或文件(通过 operand 标志变量),没有的话仅输出进程当前目录

有的话将参数中的文件和目录分开(files 和 dirs数组),然后输出信息

因为要格式化输出,所以得将目录中所有项目读取完成后并转换成字符串形式(get_stat_str)才能获得长度最长的某个信息(如,文件名,尺寸),而目录中的文件数目又事先不可知,所以用链表结构(stat_str)将所有目录中所有文件的信息存储下来。

 

 代码:

#include <stdio.h>#include <stdlib.h>#include <dirent.h>#include <time.h>#include <unistd.h>#include <sys/stat.h>#include <errno.h>#include <string.h>
#include <limits.h>#include <pwd.h>#include <grp.h>#define err_exit(func) {perror(func); exit(EXIT_FAILURE);} //打印出错函数,并结束程序#define max(a,b) ((a) > (b) ? (a) : (b))typedef struct stat_str{ //字符串形式的stat信息 char fname[name_MAX]; //大概长度,不严谨 char user[20]; char group[20]; char size[16]; char time[13]; char mode[11]; char nlink[5]; struct stat_str *next; //链表结构,因为需要预先获得目录里的所有项(为了控制格式化),但数目不确定,所以用链表保存} st_str;int nlink_maxlen = 0,user_maxlen = 0,group_maxlen = 0,size_maxlen = 0; //字符串形式的最大长度,为了格式化构造字符串int ARG_L = 0,ARG_A = 0,ARG_H = 0,ARG_D = 0; //参数是否定义//解析选项voID analyse_opt(int argc,char* argv[]);//将文件大小(字节数)转换成易读(human readable)的形式voID human_readable(off_t bytes,char *szbuf);//构造文件的详细信息voID build_line(const st_str *stat_strbuf,char* fmtstrbuf);//打印目录信息voID print_dir_info(const char *dir);//获得文件信息的str形式voID get_stat_str(const struct stat* stbuf,const char* fname,st_str *strbuf);int main(int argc,char* argv[]) { struct stat stbuf; st_str st_strbuf; int no_operand = 1; //是否没有 *** 作数 analyse_opt(argc,argv); //解析选项 //分别获得目录和其他文件,为了格式化输出 char *files[argc-1],*dirs[argc-1]; int nf = 0,nd = 0; for(int i = 1; i < argc; i++){ if(argv[i][0] == '-') //跳过选项 continue; else no_operand = 0; if(-1 == lstat(argv[i],&stbuf)) err_exit("lstat"); if(S_ISDIR(stbuf.st_mode)) dirs[nd++] = argv[i]; else files[nf++] = argv[i]; } if(no_operand){ //命令行没有输入路径 print_dir_info("."); } else { //先列出文件的信息 for(int i = 0; i < nf; i++){ char fmtstrbuf[256]; if(-1 == lstat(files[i],&stbuf)) err_exit("lstat"); get_stat_str(&stbuf,files[i],&st_strbuf); if(ARG_L){ build_line(&st_strbuf,fmtstrbuf); puts(fmtstrbuf); } else { puts(files[i]); } } //再列出目录的信息 for(int i = 0; i < nd; i++){ if(nf > 0) printf("\n%s:\n",dirs[i]); print_dir_info(dirs[i]); } } return 0;}voID analyse_opt(int argc,char* argv[]){ int opt; while((opt = getopt(argc,argv,"lahd")) != -1){ switch (opt) { case 'l': ARG_L = 1; break; case 'a': ARG_A = 1; break; case 'h': ARG_H = 1; break; case 'd': ARG_D = 1; break; } }}voID human_readable(off_t nbytes,char *szbuf){ if(nbytes < 1024) sprintf(szbuf,"%ld\0",nbytes); else if(nbytes < 1024 * 1024) sprintf(szbuf,"%.1lfK\0",(double)nbytes / 1024); else if(nbytes < 1024 * 1024 * 1024) sprintf(szbuf,"%.1lfM\0",(double)nbytes / 1024 / 1024); else sprintf(szbuf,"%.1lfG\0",(double)nbytes / 1024 / 1024 / 1024);}voID get_stat_str(const struct stat* stbuf,st_str *strbuf){ //mode sprintf(strbuf->mode,"%c%c%c%c%c%c%c%c%c%c",S_ISREG(stbuf->st_mode) ? '-' : ( S_ISDIR(stbuf->st_mode) ? 'd' : ( S_ISBLK(stbuf->st_mode) ? 'b' : ( S_ISCHR(stbuf->st_mode) ? 'c' : 'l' ) ) ),(S_IRUSR & stbuf->st_mode ) ? 'r' : '-',(S_IWUSR & stbuf->st_mode ) ? 'w' : '-',(S_IXUSR & stbuf->st_mode ) ? 'x' : '-',(S_IRGRP & stbuf->st_mode ) ? 'r' : '-',(S_IWGRP & stbuf->st_mode ) ? 'w' : '-',(S_IXGRP & stbuf->st_mode ) ? 'x' : '-',(S_IROTH & stbuf->st_mode ) ? 'r' : '-',(S_IWOTH & stbuf->st_mode ) ? 'w' : '-',(S_IXOTH & stbuf->st_mode ) ? 'x' : '-' ); //nlink sprintf(strbuf->nlink,stbuf->st_nlink); nlink_maxlen = max(nlink_maxlen,strlen(strbuf->nlink)); //user,group sprintf(strbuf->user,"%s\0",getpwuID(stbuf->st_uID)->pw_name); sprintf(strbuf->group,getgrgID(stbuf->st_gID)->gr_name); user_maxlen = max(user_maxlen,strlen(strbuf->user)); group_maxlen = max(group_maxlen,strlen(strbuf->group)); //size if(ARG_H){ char szbuf[16]; human_readable(stbuf->st_size,szbuf); sprintf(strbuf->size,szbuf); } else { sprintf(strbuf->size,stbuf->st_size); } size_maxlen = max(size_maxlen,strlen(strbuf->size)); //time strftime(strbuf->time,13,"%b %d %H:%M\0",localtime(&(stbuf->st_mtime))); //fname sprintf(strbuf->fname,fname);}voID build_line(const st_str *stat_strbuf,char* fmtstrbuf){ char fmt[32]; sprintf(fmt,"%%s %%%ds %%-%ds %%-%ds %%%ds %%s %%s\0",nlink_maxlen,user_maxlen,group_maxlen,size_maxlen); // puts(fmt); if((stat_strbuf->mode)[0] == 'd') strcat(stat_strbuf->fname,"/"); sprintf(fmtstrbuf,fmt,stat_strbuf->mode,stat_strbuf->nlink,stat_strbuf->user,stat_strbuf->group,stat_strbuf->size,stat_strbuf->time,stat_strbuf->fname);}voID print_dir_info(const char *dir){ if(ARG_D){ //显式目录本身的信息 st_str stat_strbuf; struct stat stbuf; char fmtstrbuf[256]; if(-1 == lstat(dir,&stbuf)) err_exit("lstat"); get_stat_str(&stbuf,dir,&stat_strbuf); if(ARG_L){ build_line(&stat_strbuf,fmtstrbuf); puts(fmtstrbuf); } else { puts(stat_strbuf.fname); } } else { group_maxlen = nlink_maxlen = user_maxlen = size_maxlen = 0; //列出目录所有项 struct DIR *pdir = opendir(dir); if(pdir == NulL) err_exit("opendir"); struct dirent *pdirent; struct stat stbuf; st_str * head_st_str = (st_str*)(malloc(sizeof(st_str))),*p = head_st_str; //链表头(字符串形式的stat) //循环都目录 errno = 0; while((pdirent = readdir(pdir)) != NulL){ if((pdirent->d_name)[0] != '.' || ARG_A) { //是否显示隐藏文件 if(ARG_L) { char path[256]; strcpy(path,dir); //!!!!! 找了一个多小时才找出来这个错误 strcat(path,"/"); //!!!!! d_name仅是个文件名而已 strcat(path,pdirent->d_name); //!!!!! 需要加上完整路径 if(-1 == lstat(path,&stbuf)){ err_exit("lstat"); } p->next = (st_str*)(malloc(sizeof(st_str))); p = p->next; p->next = NulL; get_stat_str(&stbuf,pdirent->d_name,p); } else { puts(pdirent->d_name); } } } if(errno != 0) err_exit("readdir"); //输出信息链表的格式化内容 p = head_st_str->next; while(ARG_L && p){ char fmtstrbuf[256]; build_line(p,fmtstrbuf); puts(fmtstrbuf); p = p->next; } if(-1 == closedir(pdir)) err_exit("closedir"); st_str *q = head_st_str->next; //释放链表 while(q){ free(head_st_str); head_st_str = q; q = q->next; } free(head_st_str); }}

  

总结

以上是内存溢出为你收集整理的Linux/UNIX编程:使用C语言实现ls命令全部内容,希望文章能够帮你解决Linux/UNIX编程:使用C语言实现ls命令所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/1015737.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-22
下一篇 2022-05-22

发表评论

登录后才能评论

评论列表(0条)

保存