英文:
Go code to list files in a Linux directory using getdents()
问题
作为练习,我想将一些使用了许多系统调用的 C 代码翻译成 Golang。我在 Unix & Linux StackExchange 上找到了这个很好的代码示例:
/*
* 使用 getdents() 列出目录,因为 ls、find 和 Python 库使用的是 readdir(),它更慢(但底层使用的是 getdents())。
*
* 编译命令:
* ]$ gcc getdents.c -o getdents
*/
#define _GNU_SOURCE
#include <dirent.h> /* 定义了 DT_* 常量 */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
struct linux_dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
#define BUF_SIZE 1024*1024*5
int
main(int argc, char *argv[])
{
int fd, nread;
char buf[BUF_SIZE];
struct linux_dirent *d;
int bpos;
char d_type;
fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
if (fd == -1)
handle_error("open");
for ( ; ; ) {
nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
if (nread == -1)
handle_error("getdents");
if (nread == 0)
break;
for (bpos = 0; bpos < nread;) {
d = (struct linux_dirent *) (buf + bpos);
d_type = *(buf + bpos + d->d_reclen - 1);
if( d->d_ino != 0 && d_type == DT_REG ) {
printf("%s\n", (char *)d->d_name );
}
bpos += d->d_reclen;
}
}
exit(EXIT_SUCCESS);
}
我只能使用 ioutil.ReadDir
来编写这段代码,这违背了初衷。有人知道如何翻译这段代码吗?
英文:
As an exercise, I wanted to translate some C code that used many syscalls into Golang. I found this nice code example on Unix & Linux StackExchange:
/*
* List directories using getdents() because ls, find and Python libraries
* use readdir() which is slower (but uses getdents() underneath.
*
* Compile with
* ]$ gcc getdents.c -o getdents
*/
#define _GNU_SOURCE
#include <dirent.h> /* Defines DT_* constants */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
struct linux_dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
#define BUF_SIZE 1024*1024*5
int
main(int argc, char *argv[])
{
int fd, nread;
char buf[BUF_SIZE];
struct linux_dirent *d;
int bpos;
char d_type;
fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
if (fd == -1)
handle_error("open");
for ( ; ; ) {
nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
if (nread == -1)
handle_error("getdents");
if (nread == 0)
break;
for (bpos = 0; bpos < nread;) {
d = (struct linux_dirent *) (buf + bpos);
d_type = *(buf + bpos + d->d_reclen - 1);
if( d->d_ino != 0 && d_type == DT_REG ) {
printf("%s\n", (char *)d->d_name );
}
bpos += d->d_reclen;
}
}
exit(EXIT_SUCCESS);
}
I have only been able to code this using ioutil.ReadDir
which defeats the purpose. Does anyone have an idea on how to translate this?
答案1
得分: 2
我会避免使用这段代码。按照现有的写法,它是错误的:在32位系统上,甚至可能在一些64位系统上,SYS_getdents
是一个不提供d_type
并且不支持64位inode号的旧系统调用,这意味着在现代文件系统上会出现不必要的错误。即使你修复了这个问题,它也不比内联readdir
更好,因为它在内部基本上做的事情是一样的。
英文:
I would avoid using this code. As written, it's wrong: on 32-bit systems and maybe even some 64-bit ones, SYS_getdents
is the legacy syscall that doesn't provide d_type
and lacks support for 64-bit inode numbers, which means you get gratuitous errors on modern filesystems. Even if you fix it, it's no better than inlining readdir
, which does basically exactly the same thing internally.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论