这是在 Ubuntu 16.04 上,使用 bash 4.3.42 和 tcsh 6.19
如果打开 X 未使用的虚拟控制台并运行 bash,我会看到 stdin、stdout、stderr 和 tty 的专用文件描述符(显然)。
$ cd /dev/fd
$ ls
0 1 2 255
$ ls -al .
... .
... ..
... 0 -> /dev/tty3
... 1 -> /dev/tty3
... 2 -> /dev/tty3
... 255 -> /dev/tty3
如果我使用 tcsh,我会看到五个指向 tty 的非 std{in, out, err} 文件描述符,并且 std{in, out, err} 全部映射到/dev/null
.
% cd /dev/fd
% ls -al
... .
... ..
... 0 -> /dev/null
... 1 -> /dev/null
... 15 -> /dev/tty3
... 16 -> /dev/tty3
... 17 -> /dev/tty3
... 18 -> /dev/tty3
... 19 -> /dev/tty3
... 2 -> /dev/null
为什么tcsh
需要这么多文件描述符都指向 tty?将 0,1 和 2 映射到 的好处是什么/dev/null
?这难道不是意味着在tcsh
分叉进程时需要稍微多一点的簿记工作,以便它们写入控制台或从控制台读取数据吗?
答案1
tcsh
的组织方式与bash
(毫不奇怪)不同。两者都很古老,并且对于细心的读者来说充满了有趣的怪癖。
tcsh
这种差异是由于管理文件描述符的方式造成的。与 不同的是bash
,它不为脚本编写者提供操作编号文件描述符的方法。开发人员发现通过以下方式组织其文件描述符很方便移动标准流进入“保存”区域(真实脚本未使用),并且在运行命令时,它重复项那些命令(即子进程),以及关闭当命令完成时它们。
在源代码中,sh.h
有这个块这解释了这些文件描述符的用法:
/*
* The shell moves std in/out/diag and the old std input away from units
* 0, 1, and 2 so that it is easy to set up these standards for invoked
* commands.
*/
#define FSAFE 5 /* We keep the first 5 descriptors untouched */
#define FSHTTY 15 /* /dev/tty when manip pgrps */
#define FSHIN 16 /* Preferred desc for shell input */
#define FSHOUT 17 /* ... shell output */
#define FSHDIAG 18 /* ... shell diagnostics */
#define FOLDSTD 19 /* ... old std input */
对于这两个 shell,(至少对于 Linux 来说)有多个指向同一个“真实”设备的链接/dev/fd
,因为这是伪终端驱动程序的组织方式。
tcsh
顺便说一句,如果您从另一个 shell运行,您将得到不同的结果。但如果您的默认 shell 是tcsh
,您可能会看到问题中描述的那些文件描述符。
答案2
这几乎肯定是您的tcsh
配置中的某些内容(例如~/.login
,~/.cshrc
/etc/csh.*
) - 查找重定向 stdin、stdout、stderr 的任何内容。
当我tcsh
在我的系统上运行时,我得到:
$ tcsh
> ls -lF /dev/fd/
total 0
lrwx------ 1 cas cas 64 Jun 26 13:28 0 -> /dev/pts/29
lrwx------ 1 cas cas 64 Jun 26 13:28 1 -> /dev/pts/29
lrwx------ 1 cas cas 64 Jun 26 13:28 2 -> /dev/pts/29
lr-x------ 1 cas cas 64 Jun 26 13:28 3 -> /proc/16570/fd/