为什么 zsh 打开的文件描述符会减一?

为什么 zsh 打开的文件描述符会减一?

我可以以正常方式显式打开文件描述符:

$ ls -lh /dev/fd/
total 0
lrwx------ 1 tavianator users 64 Jul 10 11:06 0 -> /dev/pts/6
lrwx------ 1 tavianator users 64 Jul 10 11:06 1 -> /dev/pts/6
lrwx------ 1 tavianator users 64 Jul 10 11:06 2 -> /dev/pts/6
lr-x------ 1 tavianator users 64 Jul 10 11:06 3 -> /proc/31288/fd
$ exec 3<foo
$ ls -lh /dev/fd/
total 0
lrwx------ 1 tavianator users 64 Jul 10 11:07 0 -> /dev/pts/6
lrwx------ 1 tavianator users 64 Jul 10 11:07 1 -> /dev/pts/6
lrwx------ 1 tavianator users 64 Jul 10 11:07 2 -> /dev/pts/6
lr-x------ 1 tavianator users 64 Jul 10 11:07 3 -> /home/tavianator/foo
lr-x------ 1 tavianator users 64 Jul 10 11:07 4 -> /proc/31334/fd

到目前为止,一切都很好。 zsh似乎不支持两位数文件描述符语法10<foo,但它确实支持变量替换语法{fd}<foo

$ fd=10
$ exec {fd}<foo
$ ls -lh /dev/fd/
total 0
lrwx------ 1 tavianator users 64 Jul 10 11:08 0 -> /dev/pts/6
lrwx------ 1 tavianator users 64 Jul 10 11:08 1 -> /dev/pts/6
lr-x------ 1 tavianator users 64 Jul 10 11:08 11 -> /home/tavianator/foo
lrwx------ 1 tavianator users 64 Jul 10 11:08 2 -> /dev/pts/6
lr-x------ 1 tavianator users 64 Jul 10 11:08 3 -> /home/tavianator/foo
lr-x------ 1 tavianator users 64 Jul 10 11:08 4 -> /proc/31413/fd

但是等一下,为什么是fd11打开而不是10

答案1

因为ZSH就是这么写的。默认情况下,ZSH 将文件描述符复制到 fd 10:

$ PS1='%% ' zsh -f
% lsof -p $$ | grep 10u
zsh     29192 jhqdoe   10u   CHR  136,0       0t0         3 /dev/pts/0
% 

Src/exec.c以及后续调用中与fd相关的代码movefd

/**/
static void
addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag,
      char *varid)
{
    int pipes[2];

    if (varid) {
        /* fd will be over 10, don't touch mfds */
        fd1 = movefd(fd2);
        if (fd1 == -1) {
            zerr("cannot moved fd %d: %e", fd2, errno);
            return;

其中,重复到下Src/utils.c一个可用的 above-10-which-is-already-taken-by-default-so-the-first-you'll-see-is-11 的事情:

movefd(int fd)
{
    if(fd != -1 && fd < 10) {
#ifdef F_DUPFD
        int fe = fcntl(fd, F_DUPFD, 10);
#else
        int fe = movefd(dup(fd));
#endif

我的zsh依据strace是使用fcntl代码路径,尽管我从注释中怀疑fcntl(...movefd(dup(...会导致从 11 开始的新 fd; 10 不可用,因为 zsh 默认情况下在该数字处保存有重复项。

所有的{somelabel}作用都是获取大于10的最低可用文件描述符;这可能是 11,也可能是更高的数字,具体取决于 shell 已经打开的其他内容:

% exec {foo}>asdf
% echo $foo
11
% exec {quer}>asdf
% echo $quer
12
...

相关内容