单文件编译,在命令行输入
cc myls.c -o myls
即完成编译,输入./myls -l
即可查看当前目录下的文件详细信息,其他选项类似。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#define SUBDIRSIZE 1024
#define STATBUF 1024
#define LSSHOWSIZE 1024
#define CURRENTWD 2048
#define IDBUF 1024
// 获取r w x权限
void getrwxlimits(char *buf, const struct stat *statres) {
// owner rwx
if ((S_IRUSR & statres->st_mode) == S_IRUSR) {
strncat(buf, "r", LSSHOWSIZE);
} else {
strncat(buf, "-", LSSHOWSIZE);
}
if ((S_IWUSR & statres->st_mode) == S_IWUSR) {
strncat(buf, "w", LSSHOWSIZE);
} else {
strncat(buf, "-", LSSHOWSIZE);
}
if ((S_IXUSR & statres->st_mode) == S_IXUSR) {
strncat(buf, "x", LSSHOWSIZE);
} else {
strncat(buf, "-", LSSHOWSIZE);
}
// group rwx
if ((S_IRGRP & statres->st_mode) == S_IRGRP) {
strncat(buf, "r", LSSHOWSIZE);
} else {
strncat(buf, "-", LSSHOWSIZE);
}
if ((S_IWGRP & statres->st_mode) == S_IWGRP) {
strncat(buf, "w", LSSHOWSIZE);
} else {
strncat(buf, "-", LSSHOWSIZE);
}
if ((S_IXGRP & statres->st_mode) == S_IXGRP) {
strncat(buf, "x", LSSHOWSIZE);
} else {
strncat(buf, "-", LSSHOWSIZE);
}
// othwe rwx
if ((S_IROTH & statres->st_mode) == S_IROTH) {
strncat(buf, "r", LSSHOWSIZE);
} else {
strncat(buf, "-", LSSHOWSIZE);
}
if ((S_IWOTH & statres->st_mode) == S_IWOTH) {
strncat(buf, "w", LSSHOWSIZE);
} else {
strncat(buf, "-", LSSHOWSIZE);
}
if ((S_IXOTH & statres->st_mode) == S_IXOTH) {
strncat(buf, "x", LSSHOWSIZE);
} else {
strncat(buf, "-", LSSHOWSIZE);
}
}
// 获取硬链接数
void gethlink(char *buf, nlink_t hardlinks) {
char hlinkbuf[512];
sprintf(hlinkbuf, " %ld", hardlinks);
strncat(buf, hlinkbuf, LSSHOWSIZE);
return;
}
// 获取用户名
void getusrnam(char *buf, uid_t userid) {
struct passwd *passline;
// 根据uid获取passwd文件中的一行,其中包括了用户名
passline = getpwuid(userid);
strncat(buf, "\t", LSSHOWSIZE);
strncat(buf, passline->pw_name, LSSHOWSIZE);
return;
}
// 获取组名
void getgrpnam(char *buf, gid_t grpid) {
struct group *grpline;
// getgrgid根据gid获取group文件中的一行,其中包括了组名
grpline = getgrgid(grpid);
strncat(buf, "\t", LSSHOWSIZE);
strncat(buf, grpline->gr_name, LSSHOWSIZE);
return;
}
// 写入uid
void writeuid(char *buf,uid_t userid){
char uidbuf[IDBUF];
sprintf(uidbuf,"\t%d",userid);
strncat(buf,uidbuf,LSSHOWSIZE);
}
// 写入gid
void writegid(char *buf,gid_t grpid){
char gidbuf[IDBUF];
sprintf(gidbuf,"\t%d",grpid);
strncat(buf,gidbuf,LSSHOWSIZE);
}
// 获取文件大小
void getfilesize(char *buf, off_t totalsize) {
char filesizebuf[1024];
sprintf(filesizebuf, "\t%ld", totalsize);
strncat(buf, filesizebuf, LSSHOWSIZE);
return;
}
// 获取最后的修改时间
void getmoditime(char *buf, const struct timespec *moditime) {
struct tm *tm;
char *timebuf;
char time_[20];
// 将时间戳转换成"Wed Jun 30 21:49:08 1993\n"形式字符串
timebuf = ctime(&moditime->tv_sec);
// 因为l或n选项只显示修改月、日、时、分,所有将前面和后面的字符截断
timebuf[strlen("Wed Jun 30 21:49")] = '\0';
strncpy(time_, timebuf + 4, 20);
strncat(buf, "\t", LSSHOWSIZE);
strncat(buf, time_, LSSHOWSIZE);
}
// 参数i或n调用的函数,函数中第三个参数代表i或n选项
void paralorn(const char *path, char *lsshows,int ch) {
struct stat statres;
memset(lsshows, 0, LSSHOWSIZE * sizeof(char));
if (lstat(path, &statres) < 0) {
puts("in paral");
puts(path);
perror("lstat()");
exit(1);
}
uid_t userid = statres.st_uid;
gid_t groupid = statres.st_gid;
nlink_t hardlinks = statres.st_nlink;
off_t totalsize = statres.st_size;
struct timespec moditime = statres.st_mtim;
switch (statres.st_mode & S_IFMT) {
case S_IFBLK:
strncpy(lsshows, "b", LSSHOWSIZE);
break;
case S_IFCHR:
strncpy(lsshows, "c", LSSHOWSIZE);
break;
case S_IFDIR:
strncpy(lsshows, "d", LSSHOWSIZE);
break;
case S_IFLNK:
strncpy(lsshows, "l", LSSHOWSIZE);
break;
case S_IFSOCK:
strncpy(lsshows, "s", LSSHOWSIZE);
break;
case S_IFIFO:
strncpy(lsshows, "p", LSSHOWSIZE);
break;
case S_IFREG:
strncpy(lsshows, "-", LSSHOWSIZE);
break;
default:
strncpy(lsshows, "?", LSSHOWSIZE);
break;
}
getrwxlimits(lsshows, &statres);
gethlink(lsshows, hardlinks);
if (ch == 'l') {
getusrnam(lsshows, userid);
getgrpnam(lsshows, groupid);
}else{
writeuid(lsshows,userid);
writegid(lsshows,groupid);
}
getfilesize(lsshows, totalsize);
getmoditime(lsshows, &moditime);
}
// 参数i调用的参数
void parai(const char *path,char *lsshows){
DIR *dirp;
struct dirent *dir;
struct stat statres;
char inodenum[IDBUF];
if(lstat(path,&statres)<0){
perror("lstat()");
exit(1);
}
if(S_ISDIR(statres.st_mode)){
dirp = opendir(path);
while ((dir = readdir(dirp))!=NULL){
if(strcmp(dir->d_name,".") && strcmp(dir->d_name,"..")){
memset(lsshows,0,LSSHOWSIZE);
if(lstat(dir->d_name,&statres)<0){
perror("lstat()");
exit(1);
}
sprintf(inodenum,"%ld\t",statres.st_ino);
strncpy(lsshows,inodenum,LSSHOWSIZE);
strncat(lsshows,dir->d_name,LSSHOWSIZE);
fprintf(stdout,"%s\t",lsshows);
}
}
} else{
sprintf(inodenum,"%ld\t",statres.st_ino);
strncpy(lsshows,inodenum,LSSHOWSIZE);
strncat(lsshows,path,LSSHOWSIZE);
fprintf(stdout,"%s\t",lsshows);
}
puts("");
}
// 参数a调用的参数
void paraa(const char *path,char *lsshows){
DIR *dirp;
struct dirent *dir;
struct stat statres;
if(lstat(path,&statres)<0){
perror("lstat()");
exit(1);
}
if(S_ISDIR(statres.st_mode)){
dirp = opendir(path);
while ((dir = readdir(dirp))!=NULL){
fprintf(stdout,"%s\t",dir->d_name);
}
}else{
fprintf(stdout,"%s\t",path);
}
puts("");
}
// 参数l或n调用的函数
void rfileordir(const char *path, char *lsshows, int ch) {
DIR *dirp;
struct dirent *dir;
struct stat statres;
char newpath[CURRENTWD];
// lstat获取当前文件的信息,包括文件硬链接数
if (lstat(path, &statres) < 0) {
puts("in rfileordir");
perror("lstat()");
exit(1);
}
if (S_ISDIR(statres.st_mode)) {
dirp = opendir(path);
while ((dir = readdir(dirp)) != NULL) {
if (strcmp(dir->d_name, ".") && strcmp(dir->d_name, "..")) {
memset(lsshows, 0, LSSHOWSIZE);
strncpy(newpath, path, CURRENTWD);
strncat(newpath, "/", CURRENTWD);
strncat(newpath, dir->d_name, CURRENTWD);
paralorn(newpath, lsshows,ch);
strncat(lsshows, " ", LSSHOWSIZE);
strncat(lsshows, dir->d_name, LSSHOWSIZE);
puts(lsshows);
}
}
} else {
paralorn(path, lsshows,ch);
strncat(lsshows, "\t", LSSHOWSIZE);
strncat(lsshows, path, LSSHOWSIZE);
puts(lsshows);
}
}
// 无任何参数时调用的函数
void nopara(const char *path, char *lsshows) {
DIR *dirp;
struct dirent *dir;
// 传入一个路径,打开此路径,返回一个DIR类型的结构体
dirp = opendir(path);
while ((dir = readdir(dirp)) != NULL) {// 读取目录内的文件内容保存至dirent结构体
if (strcmp(dir->d_name, ".") && strcmp(dir->d_name, ".."))
fprintf(stdout, "%s\t", dir->d_name);
}
puts("");
// 关闭目录
closedir(dirp);
}
int main(int argc, char **argv) {
// 存储结果数组
char lsshows[LSSHOWSIZE];
char currentwd[CURRENTWD];
char *pos;
// 获取当前工作目录(pwd的封装)
getcwd(currentwd, CURRENTWD);
pos = strrchr(currentwd, '/');
int c;
// 读取并分析命令行参数
c = getopt(argc, argv, "-lnia");
if(argc<2){ // 当没有命令行参数时(即只输入;一个ls时执行此项)
nopara(currentwd, lsshows);
}
while (1) {
switch (c) { // 分析命令行参数
case 1:
break;
case 'l': // 参数l显示详细信息,以用户名、组名显示
c = getopt(argc, argv, "-lnia");
if (1 == c) {
rfileordir(argv[optind - 1], lsshows, 'l');
} else {
rfileordir(currentwd, lsshows, 'l');
}
break;
case 'a': // 参数a显示当前目录下的所有文件
c = getopt(argc,argv,"-lnia");
if(1 == c){
paraa(argv[optind-1],lsshows);
}else{
paraa(currentwd,lsshows);
}
break;
case 'i': // 参数i显示inode结点号
c = getopt(argc, argv, "-lnia");
if (1 == c) {
parai(argv[optind - 1], lsshows);
} else {
parai(currentwd, lsshows);
}
break;
case 'n': // 参数n显示详细信息,以uid和gid显示
c = getopt(argc, argv, "-lnia");
if (1 == c) {
rfileordir(argv[optind - 1], lsshows, 'n');
} else {
rfileordir(currentwd, lsshows, 'n');
}
break;
default:
break;
}
if (c < 0)break;
c = getopt(argc, argv, "-lnia");
}
exit(0);
}
代码中使用到的函数介绍:
getcwd
:获取当前工作目录(get current working directory),传入参数为一块保存当前工作目录的缓冲区,和该缓冲区的大小,当前工作目录保存到该缓冲区中。
opendir、readdir、closedri
该函数族是对目录的一系列操作。
opendir
:根据传入的路径名打开目录,若打开失败返回NULL,成功返回一个DIR类型的结构体指针,类似FILE流。
readdir
:从DIR结构体中读取当前目录,返回dirent的结构体,该类型中放了文件的详细信息,包括文件名,直至所有的目录名被读完返回NULL。
closedir
:关闭当前打开的目录。
getopt
:读取并分析命令行参数,传入参数为命令行参数,以及指定要分析的字符。前面加"-“号表示读取非选项参数,若字符后边加”:"则表示可加其他参数修饰前面的选项参数。
getpwuid
根据uid获取passwd文件中的一行(包含用户名)。
getgrgid
根据gid获取group中的一行(包含组名)。
strftime
将时间格式化成指定格式字符串。具体可查阅手册内容
ctime
传入一个时间戳类型的参数,将该时间戳转换成"Wed Jun 30 21:49:08 1993\n"形式字符串。
lstat
获取当前文件信息,存储到stat结构体当中,该结构体包含了文件的多项信息,包括:文件类型、文件权限、inode号、硬链接数、uid、gid……当失败时返回-1。