我可以以正常方式显式打开文件描述符:
$ 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
...