這個學期上Linux課,老師佈置了一項作業,要我們實現ls命令。不幸,我上課沒有聽講,於是,下了幾天苦工,先在網上找博客,看人家的例子,看到用到的C函數庫函數,再用man 命令找它的用法,嘗試着把整個流程弄懂。目前我實現的ls能做 ls 和 ls -l ,其他的功能只要知道了相應的API都不是難事了,重要的是學習的過程。
相關的系統C函數主要是 readdir,opendir,closedir
相關的結構體主要是 stat,dirent,winsize (獲取終端的情況時用到)
ls:簡要方式,僅顯示文件 and/or 目錄 的名字,分欄處理,文件名按列升序排序
(列升序排序:同一行,左邊的名字比右邊的名字字典序小;同一列,上邊的名字比下邊的名字字典序小。不少Linux和Unix系統中的 ls命令都是這樣顯示的)
ls -l:詳細方式,每行顯示一個文件/目錄的詳細信息。
依次爲:類型,權限,用戶名,組名,鏈接數,文件大小,修改時間,文件名。
兩種方式下,以'.'開頭的隱藏文件均不顯示( 沒有 -a 參數嘛 ! )
直接給出源碼:
#include <grp.h>
#include <pwd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
void briefly(char*);
void amply(char*);
int cmp(const void*a , const void*b);
void print_type(struct stat e);
void print_auth(struct stat e);
void print_detail(struct stat e);
//用於文件名排序
int cmp(const void*a , const void*b ) {
return strcmp(*(char**)a,*(char**)b);
}
//簡要方式 ls
void briefly(char path[]) {
DIR* dirp;
struct dirent *dp;
struct stat e;
//path不是合法目錄,但有可能是文件
if (( (dirp=opendir(path)) == NULL)) {
if (lstat(path,&e) == -1) {
printf("\033[0;31mNo such file or directory\033[0m\n");
}
else {
char filename[256];
int tag = -1, c;
for(c = strlen(path) - 1 ; c >= 0 ; c --)
{
if(path[c] == '/') {
tag = c;
break;
}
}
int b = 0;
for(c = tag + 1 ; c < strlen(path) ; c ++)
filename[b++] = path[c];
printf("%s\n",filename);
}
return;
}
//path爲目錄
int cnt = 0,ter_width;
char * array[256];
//獲取終端列數
struct winsize size;
if( isatty (STDOUT_FILENO) == 0)
exit(1);
if(ioctl(STDOUT_FILENO,TIOCGWINSZ,&size) < 0) {
printf("\033[0;31mioctl TIOCGWINSZ error\033[0m\n");
printf("\033[0;31mNO such file or directory\033[0m\n");
exit(1);
}
ter_width = size.ws_col;
//獲取最長文件名, 對文件名排序
int max_len = 0;
while( (dp = readdir(dirp)) != NULL ) {
if(dp->d_name[0] != '.') {
int t = strlen(dp->d_name);
max_len = max_len < t ? t : max_len;
array[cnt++] = dp->d_name;
}
}
(void)closedir(dirp);
qsort(array,cnt,sizeof(array[0]),cmp);
//分欄顯示
//顯示colum : floor((ter_width+2)/(max_len+2))
//相鄰兩個文件名在終端顯示的最小距離 : 2 space
//左右相鄰的文件名在array中的offset : ceiling(cnt/colum)
int colum = (ter_width + 2)/(max_len + 2), offset;
offset = cnt%colum ==0 ? cnt/colum : cnt/colum+1;
//分欄輸出與空格填補
int i,j,k,p;
for(i = 0 ; i < offset ; i ++)
{
j = i;
while ( j < cnt ) {
printf("%s",array[j]);
if(j + offset > cnt-1 ) {
printf("\n");
break;
}
k = 0;
p = max_len - strlen(array[j]) + 2;
while (k < p) {
printf(" ");
k ++;
}
j += offset;
}
}
}
//詳細方式
void amply(char path[]) {
DIR* dirp;
struct dirent *dp;
struct stat e;
//判斷爲文件
if (((dirp=opendir(path)) == NULL)) {
if(lstat(path,&e) < 0) {
printf("\033[0;31mlstat error\033[0m\n");
printf("\033[0;31mNo such file or directory\033[0m\n");
return;
}
print_detail(e);
int c,tag = -1;
for(c = strlen(path) - 1 ; c >= 0 ; c --)
{
if(path[c] == '/') {
tag = c;
break;
}
}
//path數組加一個offset,因爲不輸出目錄,只輸出文件名
if(tag > 0)
path = path+tag+1;
printf("%s\n",path);
return;
}
//判斷爲目錄
while( (dp = readdir(dirp)) != NULL ) {
if(dp->d_name[0] == '.') {
continue;
}
char PATH[256];
//拼接PATH爲合法的相對路徑
strcpy(PATH,path);
strcat(PATH,"/");
strcat(PATH,dp->d_name);
if(lstat(PATH,&e) < 0) {
printf("\033[0;31mlstat error\033[0m\n");
continue;
}
print_detail(e);
printf("%s\n",dp->d_name);
}
(void)closedir(dirp);
}
void print_detail(struct stat e) {
char e_time[32];
struct passwd* pw; //存儲用戶名
struct group * gp; //存儲組名
//文件類型
print_type(e);
//文件權限 (所有者,同組其他用戶,其他)
print_auth(e);
printf(" ");
//用戶名和組名
pw = getpwuid(e.st_uid);
gp = getgrgid(e.st_gid);
printf("%-7s",pw->pw_name);
printf("%-7s",gp->gr_name);
//鏈接數
printf("%-4d",e.st_nlink);
//文件大小
printf("%-6d",(int)e.st_size);
//修改時間
strcpy(e_time, ctime(&e.st_mtime));
e_time[strlen(e_time)-1] = '\0';
printf("%-26s",e_time);
}
// 打印文件類型
void print_type(struct stat e) {
if (S_ISLNK(e.st_mode)) //是否爲鏈接
printf("l");
else if (S_ISREG(e.st_mode)) //是否爲文件
printf("-");
else if (S_ISDIR(e.st_mode)) //是否爲目錄
printf("d");
else if (S_ISCHR(e.st_mode)) //是否爲字符設備文件
printf("c");
else if (S_ISBLK(e.st_mode)) //是否爲塊設備文件
printf("b");
else if (S_ISFIFO(e.st_mode)) //是否爲FIFO
printf("f");
else if (S_ISSOCK(e.st_mode)) //是否爲socket
printf("s");
}
void print_auth(struct stat e) {
//所有者權限
if(e.st_mode & S_IRUSR)
printf("r");
else
printf("-");
if(e.st_mode & S_IWUSR)
printf("w");
else
printf("-");
if(e.st_mode & S_IXUSR)
printf("x");
else
printf("-");
//同組用戶權限
if(e.st_mode & S_IRGRP)
printf("r");
else
printf("-");
if(e.st_mode & S_IWGRP)
printf("w");
else
printf("-");
if(e.st_mode & S_IXGRP)
printf("x");
else
printf("-");
//其他
if(e.st_mode & S_IROTH)
printf("r");
else
printf("-");
if(e.st_mode & S_IWOTH)
printf("w");
else
printf("-");
if(e.st_mode & S_IXOTH)
printf("x");
else
printf("-");
}
int main(int argc, char*argv[]) {
//ls
if (argc == 1) {
briefly(".");
}
else if (strcmp(argv[1],"-l") == 0) {
//ls -l
if(argc == 2)
amply(".");
//ls -l ...
else {
for(int i = 2 ; i < argc ; i ++) {
amply(argv[i]);
}
}
}
//ls ...
else {
for(int i = 1 ; i < argc ; i ++)
briefly(argv[i]);
}
return 0;
}