Почему 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 

Но держитесь, почему fd 11 открыт вместо 10 ?

Потому что так написано ZSH. ZSH по умолчанию дублирует файловый дескриптор на fd 10:

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

И последующий fd-связанный код в Src/exec.c вызывает 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 делает дубликат-на-следующий-доступный-выше-10-который-уже-взял-по-умолчанию-так-в-первом-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(... приведет к появлению нового fd, начиная с 11; 10 недоступно как zsh по умолчанию имеет дубликат на этом количестве.

Все действия {somelabel} том, чтобы получить наименьший доступный дескриптор файла больше 10; это может быть 11 или вместо этого может быть некоторое большее число в зависимости от того, что еще имеет оболочка:

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