ls 命令可以說是Linux下最常用的命令之一。它有眾多的選項,其中有很多是很有用的,你是否熟悉呢?要學習如何編寫 ls 命令,首先我們要先了解它怎么使用。
我們在 Linux常用命令和vi,gdb的使用 第一節中就已經提到過 ls 命令的使用,下面我們先就 ls 命令做詳細的介紹
1.ls命令詳細介紹
下面列出了 ls 命令的絕大多數選項。
-a 列出目錄下的所有文件,包括以 . 開頭的隱含文件。
-b 把文件名中不可輸出的字符用反斜杠加字符編號(就象在C語言里一樣)的形式列出。
-c 輸出文件的 i 節點的修改時間,並以此排序。
-d 將目錄象文件一樣顯示,而不是顯示其下的文件。
-e 輸出時間的全部信息,而不是輸出簡略信息。
-f -U 對輸出的文件不排序。
-g 無用。
-i 輸出文件的 i 節點的索引信息。
-k 以 k 字節的形式表示文件的大小。
-l 列出文件的詳細信息。
-m 橫向輸出文件名,並以“,”作分格符。
-n 用數字的 UID,GID 代替名稱。
-o 顯示文件的除組信息外的詳細信息。
-p -F 在每個文件名后附上一個字符以說明該文件的類型,“*”表示可執行的普通
文件;“/”表示目錄;“@”表示符號鏈接;“|”表示FIFOs;“=”表示套
接字(sockets)。
-q 用?代替不可輸出的字符。
-r 對目錄反向排序。
-s 在每個文件名后輸出該文件的大小。
-t 以時間排序。
-u 以文件上次被訪問的時間排序。
-x 按列輸出,橫向排序。
-A 顯示除 “.”和“..”外的所有文件。
-B 不輸出以 “~”結尾的備份文件。
-C 按列輸出,縱向排序。
-G 輸出文件的組的信息。
-L 列出鏈接文件名而不是鏈接到的文件。
-N 不限制文件長度。
-Q 把輸出的文件名用雙引號括起來。
-R 列出所有子目錄下的文件。
-S 以文件大小排序。
-X 以文件的擴展名(最后一個 . 后的字符)排序。
-1 一行只輸出一個文件。
--color=no 不顯示彩色文件名
--help 在標准輸出上顯示幫助信息。
--version 在標准輸出上輸出版本信息並退出。
---------------------------------------------------------------------
2.ls命令實現過程用到的函數
函數原型:
1)int stat(const char *path, struct stat *buf);
stat函數 將path(文件)的屬性信息保存在 buf結構體中
2)struct passwd *getpwuid(uid_t uid);
getpwuid函數是通過用戶的uid查找用戶的passwd數據,其中包括username
3)struct group *getgrgid(gid_t gid);
getgrgid函數通過用戶組gid指定的組識別碼逐一搜索組文件,找到時便將該組的數據以group結構返回。
4)struct tm *localtime(const time_t *timep);
localtime函數把從1970-1-1零點零分到當前時間系統所偏移的秒數時間轉換為日歷時間 。並且此函數獲得的tm結構體的時間,是已經進行過時區轉化為本地時間。
5) DIR *opendir(const char *name);
opendir()函數的作用是:打開目錄句柄,返回一個文件描述符。
6) struct dirent *readdir(DIR *dirp);
readdir()函數讀取一個目錄文件描述符的信息,將信息返回到dirent結構體中。
struct stat 內容:
/* The stat structure. */ struct stat { unsigned long st_dev; /* Device. */ unsigned long st_ino; /* File serial number. */ unsigned int st_mode; /* File mode. */ unsigned int st_nlink; /* Link count. */ unsigned int st_uid; /* User ID of the file's owner. */ unsigned int st_gid; /* Group ID of the file's group. */ unsigned long st_rdev; /* Device number, if device. */ unsigned long __pad1; long st_size; /* Size of file, in bytes. */ int st_blksize; /* Optimal block size for I/O. */ int __pad2; long st_blocks; /* Number 512-byte blocks allocated. */ int st_atime; /* Time of last access. */ unsigned int st_atime_nsec; int st_mtime; /* Time of last modification. */ unsigned int st_mtime_nsec; int st_ctime; /* Time of last status change. */ unsigned int st_ctime_nsec; unsigned int __unused4; unsigned int __unused5; };
struct stat中的st_mode值各個位代表的意思:
The following flags are defined for the st_mode field: /* 是什么類型的文件 */ S_IFMT 0170000 bit mask for the file type bit fields S_IFSOCK 0140000 socket S_IFLNK 0120000 symbolic link S_IFREG 0100000 regular file S_IFBLK 0060000 block device S_IFDIR 0040000 directory S_IFCHR 0020000 character device S_IFIFO 0010000 FIFO S_ISUID 0004000 set UID bit S_ISGID 0002000 set-group-ID bit (see below) S_ISVTX 0001000 sticky bit (see below)
/* 是否有可讀寫權限 */ S_IRWXU 00700 mask for file owner permissions S_IRUSR 00400 owner has read permission S_IWUSR 00200 owner has write permission S_IXUSR 00100 owner has execute permission S_IRWXG 00070 mask for group permissions S_IRGRP 00040 group has read permission S_IWGRP 00020 group has write permission S_IXGRP 00010 group has execute permission S_IRWXO 00007 mask for permissions for others (not in group) S_IROTH 00004 others have read permission S_IWOTH 00002 others have write permission S_IXOTH 00001 others have execute permission
struct passwd 內容:
/* The passwd structure. */ struct passwd { char *pw_name; /* Username. */ char *pw_passwd; /* Password. */ __uid_t pw_uid; /* User ID. */ __gid_t pw_gid; /* Group ID. */ char *pw_gecos; /* Real name. */ char *pw_dir; /* Home directory. */ char *pw_shell; /* Shell program. */ };
struct group 內容:
/* The group structure. */ struct group { char *gr_name; /* Group name. */ char *gr_passwd; /* Password. */ __gid_t gr_gid; /* Group ID. */ char **gr_mem; /* Member list. */ };
struct tm 內容:
/* Used by other time functions. */ struct tm { int tm_sec; /* Seconds. [0-60] (1 leap second) */ int tm_min; /* Minutes. [0-59] */ int tm_hour; /* Hours. [0-23] */ int tm_mday; /* Day. [1-31] */ int tm_mon; /* Month. [0-11] */ int tm_year; /* Year - 1900. */ int tm_wday; /* Day of week. [0-6] */ int tm_yday; /* Days in year.[0-365] */ int tm_isdst; /* DST. [-1/0/1]*/ #ifdef __USE_BSD long int tm_gmtoff; /* Seconds east of UTC. */ __const char *tm_zone; /* Timezone abbreviation. */ #else long int __tm_gmtoff; /* Seconds east of UTC. */ __const char *__tm_zone; /* Timezone abbreviation. */ #endif };
struct dirent內容:
struct dirent { #ifndef __USE_FILE_OFFSET64 __ino_t d_ino; __off_t d_off; #else __ino64_t d_ino; __off64_t d_off; #endif unsigned short int d_reclen; unsigned char d_type; char d_name[256]; /* We must not include limits.h! */ };
---------------------------------------------------------------------
3.ls命令的實現
1)首先獲得文件的屬性
const char *path由main函數傳入:
int main(int argc, char **argv)
獲取文件argv[1]的屬性信息,並保存在st結構體中:
if(stat(argv[1], &st) < 0) { perror("stat"); return -1; }
2)判斷是否是目錄
if((st.st_mode & S_IFMT) == S_IFDIR) display_dir(argv[1]); else display_file(argv[1], argv[1]);
如果是文件則直接獲取文件信息即可,如果是目錄則要進入到目錄中進行遍歷,我們封裝兩個函數display_file()和display_dir()來分別實現
3)目錄與文件的實現
【display_file()】
a.由屬性信息獲得文件的類型
用stat結構體中的st_mode與掩碼S_IFMT相與,得到文件類型:
switch(st.st_mode & S_IFMT) { case S_IFREG: printf("-"); break; case S_IFDIR: printf("d"); break; case S_IFLNK: printf("l"); break; case S_IFBLK: printf("b"); break; case S_IFCHR: printf("c"); break; case S_IFIFO: printf("p"); break; case S_IFSOCK: printf("s"); break; }
b.由屬性信息獲得文件的可讀寫權限
for(i = 8; i >= 0; i--) { if(st.st_mode & (1 << i)) { switch(i%3) { case 2: printf("r"); break; case 1: printf("w"); break; case 0: printf("x"); break; } } else printf("-"); }
c.由屬性信息獲得文件的owner和所在的group信息
pw = getpwuid(st.st_uid);
gr = getgrgid(st.st_gid);
d.最后文件所以信息可以打印
printf("%2d %s %s %4ld", st.st_nlink, pw->pw_name, gr->gr_name, st.st_size); tm = localtime(&st.st_ctime); printf(" %04d-%02d-%02d %02d:%02d",tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); printf(" %s\n", filename);
【display_dir()】
獲取目錄文件描述符,並且讀取目錄信息,判斷目錄不為空則繼續讀取。如果讀取到的是目錄,則遞歸調用,否則直接調用display_file()即可。
dir = opendir(dirname); while((dirent = readdir(dir)) != NULL) { strcpy(buf, dirname); strcat(buf, "/"); strcat(buf, dirent->d_name); if(stat(buf, &st)) { perror("stat"); return -1; } if(dirent->d_name[0] != '.') display_file(buf, dirent->d_name); }
---------------------------------------------------------------------
4.結果顯示
查看文件(display_file)結果:
linux@ubuntu:~/test$ ./a.out stat.c -rw-r--r-- 1 linux linux 15316 2012-06-19 14:05 stat.c
查看文件夾(display_dir)結果:
linux@ubuntu:~/test$ ./a.out . -rw-r--r-- 1 linux linux 1508 2012-06-19 14:40 ls.c -rw-r--r-- 1 linux linux 1635 2012-06-19 14:12 ls-bak.c -rwxrw-rw- 1 linux linux 1777 2012-06-19 15:02 ls_t.c -rw-r--r-- 1 linux linux 15316 2012-06-19 14:05 stat.c -rwxr-xr-x 1 linux linux 7632 2012-06-19 15:14 a.out
附:ls 命令實現的完整代碼
#include <stdio.h> #include <sys/types.h> #include <dirent.h> #include <sys/stat.h> #include <string.h> #include <unistd.h> #include <grp.h> #include <pwd.h> #include <time.h> int display_file(char *path, char *filename) { struct stat st; int i; struct passwd *pw; struct group *gr; struct tm *tm; stat(path, &st); switch(st.st_mode & S_IFMT) { case S_IFREG: printf("-"); break; case S_IFDIR: printf("d"); break; case S_IFLNK: printf("l"); break; case S_IFBLK: printf("b"); break; case S_IFCHR: printf("c"); break; case S_IFIFO: printf("p"); break; case S_IFSOCK: printf("s"); break; } for(i = 8; i >= 0; i--) { if(st.st_mode & (1 << i)) { switch(i%3) { case 2: printf("r"); break; case 1: printf("w"); break; case 0: printf("x"); break; } } else printf("-"); } pw = getpwuid(st.st_uid); gr = getgrgid(st.st_gid); printf("%2d %s %s %4ld", st.st_nlink, pw->pw_name, gr->gr_name, st.st_size); tm = localtime(&st.st_ctime); printf(" %04d-%02d-%02d %02d:%02d",tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); printf(" %s\n", filename); return 0; } int display_dir(char *dirname) { DIR *dir; struct dirent *dirent; struct stat st; char buf[1024]; dir = opendir(dirname); while((dirent = readdir(dir)) != NULL) { strcpy(buf, dirname); strcat(buf, "/"); strcat(buf, dirent->d_name); if(stat(buf, &st)) { perror("stat"); return -1; } if(dirent->d_name[0] != '.') display_file(buf, dirent->d_name); } } int main(int argc, char **argv) { struct stat st; char buf[1024]; if(stat(argv[1], &st) < 0) { perror("stat"); return -1; } if((st.st_mode & S_IFMT) == S_IFDIR) display_dir(argv[1]); else display_file(argv[1], argv[1]); return 0; }
關於如何顯示文件顏色的相關介紹可以參看:http://www.embedu.org/Column/Column341.htm
效果圖如下: