实现ls -aln功能的C程序输出异常,请求排查修复
问题描述
我写了一个实现ls -aln命令功能的C程序,但出了问题。
预期输出
drwxr-xr-x@ 6 seven staff 192 19 fév 18:58 . drwxr-xr-x@ 4 seven staff 128 6 fév 00:37 .. drwxr-xr-x@ 7 seven staff 224 17 fév 15:00 .idea -rw-r--r--@ 1 seven staff 104 5 fév 17:37 CMakeLists.txt drwxr-xr-x@ 11 seven staff 352 19 fév 18:58 cmake-build-debug -rw-r--r--@ 1 seven staff 2144 19 fév 18:58 main.c
实际输出
d-----rwxr-xr-x 11 352 seven staff . d-----rwxr-xr-x 6 192 seven staff .. d-----rwxr-xr-x 6 192 seven staff cmake-build-debug d-----rwxr-xr-x 6 192 seven staff CMakeLists.txt d-----rwxr-xr-x 6 192 seven staff main.c d-----rwxr-xr-x 6 192 seven staff .idea
问题表现
- 链接数不符合预期
- 文件大小不正确
- 权限显示异常(所有文件都被标记为目录,且权限位前多了多余的
-----) - 缺少文件修改时间字段
我怀疑是printf的问题,但不知道怎么修复,以下是我的代码:
int main() { char* curr_dir = NULL; DIR* dir = NULL; struct dirent* entity = NULL; struct stat fileStat; struct passwd* pwd; struct group* gid; curr_dir = getenv("PWD");//current directory with PWD if (curr_dir==NULL){ printf("ERROR: j'ai pas pris le repertoire courrant"); } dir = opendir("/Users/seven/Desktop/Programmazione/TP"); if(dir == NULL) { printf("ERROR: j'ai pas pue ouvrir le repertoire"); } while((entity = readdir(dir)) != NULL){ stat(entity->d_name, &fileStat); printf((S_ISDIR(fileStat.st_mode)) ? "d" : "-"); printf((S_ISLNK(fileStat.st_mode)) ? "l" : "-"); printf((S_ISSOCK(fileStat.st_mode)) ? "s" : "-"); printf((S_ISFIFO(fileStat.st_mode)) ? "p" : "-"); printf((S_ISBLK(fileStat.st_mode)) ? "b" : "-"); printf((S_ISCHR(fileStat.st_mode)) ? "c" : "-"); printf((fileStat.st_mode & S_IRUSR) ? "r" : "-"); printf((fileStat.st_mode & S_IWUSR) ? "w" : "-"); printf((fileStat.st_mode & S_IXUSR) ? "x" : "-"); printf((fileStat.st_mode & S_IRGRP) ? "r" : "-"); printf((fileStat.st_mode & S_IWGRP) ? "w" : "-"); printf((fileStat.st_mode & S_IXGRP) ? "x" : "-"); printf((fileStat.st_mode & S_IROTH) ? "r" : "-"); printf((fileStat.st_mode & S_IWOTH) ? "w" : "-"); printf((fileStat.st_mode & S_IXOTH) ? "x" : "-"); printf("\t %u", fileStat.st_nlink); printf("\t %lld", fileStat.st_size); pwd = getpwuid(fileStat.st_uid); gid = getgrgid(fileStat.st_gid); if(pwd != NULL && gid != NULL ){ printf("\t %s", pwd->pw_name); printf("\t %s", gid->gr_name); } printf("\t %s\n", entity->d_name); } closedir(dir); }
问题分析与修复
1. 文件类型判断逻辑错误
当前代码会把所有文件类型的判断结果都输出一遍(不满足就输出-),导致权限位前多了5个多余的-。正确逻辑是只输出一个对应文件类型的字符:
// 仅输出一个文件类型标识 if (S_ISDIR(fileStat.st_mode)) printf("d"); else if (S_ISLNK(fileStat.st_mode)) printf("l"); else if (S_ISSOCK(fileStat.st_mode)) printf("s"); else if (S_ISFIFO(fileStat.st_mode)) printf("p"); else if (S_ISBLK(fileStat.st_mode)) printf("b"); else if (S_ISCHR(fileStat.st_mode)) printf("c"); else printf("-");
2. stat调用未使用完整路径,导致属性读取错误
你打开的是指定目录,但调用stat时只用了文件名,若程序当前工作目录不是目标目录,stat会找不到文件,导致fileStat保留上一次的旧数据,这就是所有文件属性重复的原因。解决方法是拼接完整路径:
char full_path[PATH_MAX]; snprintf(full_path, sizeof(full_path), "%s/%s", "/Users/seven/Desktop/Programmazione/TP", entity->d_name); if (stat(full_path, &fileStat) == -1) { perror("stat failed"); continue; }
3. 输出格式未对齐,缺少时间字段
ls -aln用固定宽度对齐输出,\t会导致排版混乱;同时代码未输出文件修改时间,需用strftime格式化st_mtime。
4. 变量类型匹配问题
st_nlink类型是nlink_t,st_size是off_t,需用对应格式符避免输出错误,比如%4u、%8lld。
修正后的完整代码
#include <stdio.h> #include <dirent.h> #include <sys/stat.h> #include <pwd.h> #include <grp.h> #include <time.h> #include <string.h> #include <errno.h> #include <limits.h> int main() { const char* target_dir = "/Users/seven/Desktop/Programmazione/TP"; DIR* dir = opendir(target_dir); if (dir == NULL) { perror("Failed to open directory"); return 1; } struct dirent* entity; struct stat fileStat; struct passwd* pwd; struct group* gid; char full_path[PATH_MAX]; char time_buf[64]; while ((entity = readdir(dir)) != NULL) { // 拼接完整文件路径 snprintf(full_path, sizeof(full_path), "%s/%s", target_dir, entity->d_name); if (stat(full_path, &fileStat) == -1) { fprintf(stderr, "stat %s failed: %s\n", entity->d_name, strerror(errno)); continue; } // 输出文件类型 if (S_ISDIR(fileStat.st_mode)) printf("d"); else if (S_ISLNK(fileStat.st_mode)) printf("l"); else if (S_ISSOCK(fileStat.st_mode)) printf("s"); else if (S_ISFIFO(fileStat.st_mode)) printf("p"); else if (S_ISBLK(fileStat.st_mode)) printf("b"); else if (S_ISCHR(fileStat.st_mode)) printf("c"); else printf("-"); // 输出权限位 printf((fileStat.st_mode & S_IRUSR) ? "r" : "-"); printf((fileStat.st_mode & S_IWUSR) ? "w" : "-"); printf((fileStat.st_mode & S_IXUSR) ? "x" : "-"); printf((fileStat.st_mode & S_IRGRP) ? "r" : "-"); printf((fileStat.st_mode & S_IWGRP) ? "w" : "-"); printf((fileStat.st_mode & S_IXGRP) ? "x" : "-"); printf((fileStat.st_mode & S_IROTH) ? "r" : "-"); printf((fileStat.st_mode & S_IWOTH) ? "w" : "-"); printf((fileStat.st_mode & S_IXOTH) ? "x" : "-"); // 输出macOS扩展属性标记@ struct statfs fs_stat; if (statfs(full_path, &fs_stat) == 0 && (fs_stat.f_flags & MNT_DONTBROWSE)) { printf("@"); } else { printf(" "); } // 输出链接数、用户/组名、文件大小 printf(" %4u ", fileStat.st_nlink); pwd = getpwuid(fileStat.st_uid); gid = getgrgid(fileStat.st_gid); printf("%-8s %-8s ", pwd ? pwd->pw_name : "unknown", gid ? gid->gr_name : "unknown"); printf("%8lld ", (long long)fileStat.st_size); // 输出格式化后的修改时间 strftime(time_buf, sizeof(time_buf), "%d %b %H:%M", localtime(&fileStat.st_mtime)); printf("%s ", time_buf); // 输出文件名 printf("%s\n", entity->d_name); } closedir(dir); return 0; }
修复效果说明
- 修正文件类型判断,移除多余的
----- - 使用完整路径调用
stat,获取正确的文件属性 - 固定宽度格式化输出,对齐方式与
ls -aln一致 - 添加文件修改时间输出
- 处理
stat调用失败的异常情况
内容的提问来源于stack exchange,提问作者SeveN
相关产品推荐
相关产品推荐

