Linux/UNIX編程:使用C語言實現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, const char* fname, 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, "%ld\0", 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, "%s\0", 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, "%s\0", szbuf); } else { sprintf(strbuf->size, "%ld\0", 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, "%s\0", 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); } }

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM