Can';不要在MacOS上的`/dev/fd`上调用'ioutil.ReadDir'
我尝试运行以下Go代码:Can';不要在MacOS上的`/dev/fd`上调用'ioutil.ReadDir',macos,go,Macos,Go,我尝试运行以下Go代码: package main import ( "fmt" "io/ioutil" ) func main() { items, err := ioutil.ReadDir("/dev/fd") if err != nil { panic(err) } fmt.Println(items) } 我刚刚得到这个错误: panic: lstat /dev/fd/4: bad file descriptor
package main
import (
"fmt"
"io/ioutil"
)
func main() {
items, err := ioutil.ReadDir("/dev/fd")
if err != nil {
panic(err)
}
fmt.Println(items)
}
我刚刚得到这个错误:
panic: lstat /dev/fd/4: bad file descriptor
goroutine 1 [running]:
main.main()
/Users/andy/Desktop/demo.go:11 +0xe8
exit status 2
/dev/fd
文件夹肯定存在,当我ls
它时,里面有一个/dev/fd/4
$ ls -Al /dev/fd
total 9
crw--w---- 1 andy tty 16, 4 Jan 25 00:16 0
crw--w---- 1 andy tty 16, 4 Jan 25 00:16 1
crw--w---- 1 andy tty 16, 4 Jan 25 00:16 2
dr--r--r-- 3 root wheel 4419 Jan 23 20:42 3/
dr--r--r-- 1 root wheel 0 Jan 23 20:42 4/
发生什么事了?为什么我不能读这个目录?我正在尝试将ls
命令移植到Go,因此我希望能够读取此目录,以便生成与ls
类似的输出
编辑:我以非root用户的身份运行所有内容。设置了/dev/fd
上的可执行位
假设您以非root用户身份运行代码,我猜问题在于目录没有设置execute位,这会阻止
chdir
-ing到该目录(在尝试读取内容之前,ReadDir
可能会这样做)。首先,让我们记住/dev/fd是特殊的。它的内容对于每个进程都是不同的(因为它们反映了该进程的文件描述符),因此它实际上并不意味着ls
可以列出它,因为它的内容对于ls
和您的程序是不同的
无论如何,这里有一个稍微更新的程序版本,我们不让ioutil在背后做事情,而是自己做,看看发生了什么:
package main
import (
"fmt"
"time"
"os"
"log"
)
func main() {
d, err := os.Open("/dev/fd")
if err != nil {
log.Fatal(err)
}
names, err := d.Readdirnames(0)
if err != nil {
log.Fatal(err)
}
for _, n := range names {
n = "/dev/fd/" + n
fmt.Printf("file: %s\n", n)
_, err := os.Lstat(n)
if err != nil {
fmt.Printf("lstat error: %s - %v\n", n, err)
}
}
time.Sleep(time.Second * 200)
}
然后当运行时,它会给我:
file: /dev/fd/0
file: /dev/fd/1
file: /dev/fd/2
file: /dev/fd/3
file: /dev/fd/4
lstat error: /dev/fd/4 - lstat /dev/fd/4: bad file descriptor
所以这确实是同一个问题。我在最后添加了睡眠,这样进程就不会死,这样我们就可以调试它有哪些文件描述符。在另一个终端:
$ lsof -p 7861
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
foo 7861 art cwd DIR 1,4 2272 731702 /Users/art/src/go/src
foo 7861 art txt REG 1,4 1450576 8591078117 /private/var/folders/m7/d614cd9x61s0l3thb7cf3rkh0000gn/T/go-build268777304/command-line-arguments/_obj/exe/foo
foo 7861 art txt REG 1,4 837248 8590944844 /usr/lib/dyld
foo 7861 art 0u CHR 16,4 0t8129 645 /dev/ttys004
foo 7861 art 1u CHR 16,4 0t8129 645 /dev/ttys004
foo 7861 art 2u CHR 16,4 0t8129 645 /dev/ttys004
foo 7861 art 3r DIR 37,7153808 0 316 /dev/fd
foo 7861 art 4u KQUEUE count=0, state=0x8
我们可以看到fd4是一个KQUEUE。Kqueue文件描述符用于管理OSX上的事件,如果您知道该机制,类似于Linux上的epoll
OSX似乎不允许对/dev/fd
中的kqueue文件描述符使用stat
,我们可以用以下方法进行验证:
$ cat > foo.c
#include <sys/types.h>
#include <sys/event.h>
#include <sys/stat.h>
#include <stdio.h>
#include <err.h>
int
main(int argc, char **argv)
{
int fd = kqueue();
char path[16];
struct stat st;
snprintf(path, sizeof(path), "/dev/fd/%d", fd);
if (lstat(path, &st) == -1)
err(1, "lstat");
return 0;
}
$ cc -o foo foo.c && ./foo
foo: lstat: Bad file descriptor
$
$cat>foo.c
#包括
#包括
#包括
#包括
#包括
int
主(内部argc,字符**argv)
{
int fd=kqueue();
字符路径[16];
结构统计;
snprintf(路径,sizeof(路径),“/dev/fd/%d”,fd);
if(lstat(path,&st)=-1)
错误(1,“lstat”);
返回0;
}
$cc-o foo foo.c&./foo
foo:lstat:错误的文件描述符
$
OSX上的Go程序需要一个kqueue来处理各种事件(不确定是哪个,但可能是信号、计时器和各种文件事件)
这里有四个选项:不要统计,忽略错误,不要触摸/dev/fd,因为它很奇怪,让苹果相信这是一个bug。命令的输出是什么
stat/dev/fd/4
或stat-L/dev/fd/4
?/dev/fd
很少直接交互,当OSX作为一个常规文件系统处理时,它似乎有很多优点。不过,如果您想确切了解它在做什么,请尝试strace ls-li/dev/fd
。通常,建议从常规文件处理中排除/dev/fd
(甚至/dev
)。stat/dev/fd/4
会产生相同的错误:stat:Bad file descriptor
。我的机器上似乎没有strace
命令。是的,我以非root用户的身份运行所有操作。不过,执行位已设置。请参阅编辑。@AndyCarlson为了清楚起见,我刚才说的是/dev/fd/4
-上的执行位,因为错误引用的是执行位,而不是其父级。我的理解是,打开目录需要执行位。但是我甚至不能统计目录。我应该仍然能够在没有-x
位的情况下stat
a dir。
$ cat > foo.c
#include <sys/types.h>
#include <sys/event.h>
#include <sys/stat.h>
#include <stdio.h>
#include <err.h>
int
main(int argc, char **argv)
{
int fd = kqueue();
char path[16];
struct stat st;
snprintf(path, sizeof(path), "/dev/fd/%d", fd);
if (lstat(path, &st) == -1)
err(1, "lstat");
return 0;
}
$ cc -o foo foo.c && ./foo
foo: lstat: Bad file descriptor
$