为了模仿默认的“ls -1”行为,通过调用以下方法使您的程序支持区域设置:
setlocale(LC_ALL, "");
在您的
main()
函数开始附近使用,并且使用。
count = scandir(dir, &array, my_filter, alphasort);
其中my_filter()
是一个函数,对于以点号.
开头的名称返回0,对于其他名称返回1。alphasort()
是一个使用本地排序顺序(与strcoll()
相同顺序)的POSIX函数。
基本实现应该如下:
#define _POSIX_C_SOURCE 200809L
#define _ATFILE_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <locale.h>
#include <string.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
static void my_print(const char *name, const struct stat *info)
{
printf("%s\n", name);
}
static int my_filter(const struct dirent *ent)
{
if (ent->d_name[0] == '.')
return 0;
return 1;
}
static int my_ls(const char *dir)
{
struct dirent **list = NULL;
struct stat info;
DIR *dirhandle;
int size, i, fd;
size = scandir(dir, &list, my_filter, alphasort);
if (size == -1) {
const int cause = errno;
if (cause == ENOTDIR && lstat(dir, &info) == 0) {
my_print(dir, &info);
return 0;
}
fprintf(stderr, "%s: %s.\n", dir, strerror(cause));
return -1;
}
dirhandle = opendir(dir);
if (!dirhandle) {
fprintf(stderr, "%s: %s\n", dir, strerror(errno));
fd = AT_FDCWD;
} else {
fd = dirfd(dirhandle);
}
for (i = 0; i < size; i++) {
struct dirent *ent = list[i];
if (fstatat(fd, ent->d_name, &info, AT_SYMLINK_NOFOLLOW) == -1) {
fprintf(stderr, "%s: %s.\n", ent->d_name, strerror(errno));
memset(&info, 0, sizeof info);
}
my_print(ent->d_name, &info);
}
if (dirhandle)
closedir(dirhandle);
for (i = 0; i < size; i++)
free(list[i]);
free(list);
return 0;
}
int main(int argc, char *argv[])
{
int arg;
setlocale(LC_ALL, "");
if (argc > 1) {
for (arg = 1; arg < argc; arg++) {
if (my_ls(argv[arg])) {
return EXIT_FAILURE;
}
}
} else {
if (my_ls(".")) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
请注意,我特意将这个代码复杂化,不仅仅是为了满足您的需求,更重要的是我不希望您只是简单地复制和粘贴代码。相比于试图向您的老师/讲师/助教解释为什么代码看起来像是从其他地方完全复制下来的,编译、运行和调查这个程序并将所需的更改(可能仅限于一行“setlocale("", LC_ALL);”代码!)移植到您自己的程序中会更容易些。
上面的代码甚至可以处理在命令行上指定的文件(“cause == ENOTDIR”部分)。它还使用一个单一的函数“my_print(const char *name, const struct stat *info)”来打印每个目录条目;为了实现这一点,它确实调用了每个条目的“stat”。
与构建目录条目路径并调用“lstat()”不同,“my_ls()”打开一个目录句柄,并使用“fstatat(descriptor, name, struct stat *, AT_SYMLINK_NOFOLLOW)”以基本相同的方式收集信息,就像“lstat()”那样,但是“name”是以“descriptor”指定的目录为起点的相对路径(如果“handle”是一个开放的“DIR *”,则是“dirfd(handle)”)。
的确,为每个目录条目调用一个stat函数是“缓慢的”(特别是如果您需要“/bin/ls -1”样式的输出)。但是,“ls”的输出是为人类消费而设计的;而且很多时候通过“more”或“less”管道传递,以便让人类可以悠闲地查看它。这就是为什么我个人认为这里“额外”的stat()调用(即使不是真正需要的)并不是一个问题。我所知道的大多数人类用户倾向于使用“ls -l”或(我的最爱)“ls -laF --color=auto”。 (“auto”意味着仅当标准输出是终端时才使用ANSI颜色;即当“isatty(fileno(stdout)) == 1”时。)
换句话说,现在您已经得到了“ls -1”命令,我建议您将输出修改为类似于“ls -l”(短横线ell,而不是短横线one)。您只需修改“my_print()”即可实现。
ls
uses its own custom string comparison whilealphasort
is just an implementation ofstrcmp
- MD XF