Linux中的proc/pid/fd标准输出是11吗?

6
执行一个将stdout重定向到文件的脚本。因此,/proc/$$/fd/1应该指向该文件(因为stdout fileno为1)。然而,该文件的实际fd是11。请解释一下为什么会这样。
以下是会话内容:
$ cat hello.sh
#!/bin/sh -e
ls -l /proc/$$/fd >&2

$ ./hello.sh > /tmp/1
total 0
lrwx------ 1 nga users 64 May 28 22:05 0 -> /dev/pts/0
lrwx------ 1 nga users 64 May 28 22:05 1 -> /dev/pts/0
lr-x------ 1 nga users 64 May 28 22:05 10 -> /home/me/hello.sh
l-wx------ 1 nga users 64 May 28 22:05 11 -> /tmp/1
lrwx------ 1 nga users 64 May 28 22:05 2 -> /dev/pts/0

如果你执行 ls -l /proc/self/fd,它会做什么? - Random832
2个回答

5
我有一个怀疑,但这高度依赖于您的shell的行为方式。您看到的文件描述符是:
  • 0:标准输入
  • 1:标准输出
  • 2:标准错误
  • 10:正在运行的脚本
  • 11:脚本的正常标准输出的备份副本
描述符10和11在执行时关闭,因此不会出现在ls进程中。然而,在分叉之前,0-2已经为ls准备好了。我在dash(Debian Almquist shell)中看到了这种行为,但在bash(Bourne again shell)中没有。Bash在分叉后进行文件描述符操作,并且偶然使用255而不是10来表示脚本。在分叉后进行更改意味着它不必在父进程中恢复描述符,因此它没有多余的副本可供dup2。

我明白了,谢谢!确实是破折号的问题,而且bash和zsh表现出了“正确”的行为。 - stepancheg
1
这并没有什么不妥之处;只是不太高效。此外,它可能需要用于内部命令;Bash可能需要更多的逻辑来处理这些命令。 - Yann Vernier

3
strace的输出对此很有帮助。
相关部分如下:
fcntl64(1, F_DUPFD, 10)                 = 11
close(1)                                = 0
fcntl64(11, F_SETFD, FD_CLOEXEC)        = 0
dup2(2, 1)                              = 1
stat64("/home/random/bin/ls", 0xbf94d5e0) = -1 ENOENT (No such file or
+++++++>directory)
stat64("/usr/local/bin/ls", 0xbf94d5e0) = -1 ENOENT (No such file or directory)
stat64("/usr/bin/ls", 0xbf94d5e0)       = -1 ENOENT (No such file or directory)
stat64("/bin/ls", {st_mode=S_IFREG|0755, st_size=96400, ...}) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
+++++++>child_tidptr=0xb75a8938) = 22748
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 22748
--- SIGCHLD (Child exited) @ 0 (0) ---
dup2(11, 1)                             = 1

因此,shell将现有的stdout移动到一个可用的文件描述符(即11)上,然后将现有的stderr移动到其自己的stdout上(由于>&2重定向),然后在ls命令完成时将11恢复为其自己的stdout。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接