gawk5.1.1
源码的 awk.h
文件定义了 redirect
这个「重定向」结构体,里面有个 redirect_flags
标记枚举,有这几个值:
1 | RED_NONE = 0, |
后面主要关注 RED_FILE
、RED_WRITE
、RED_USED
这 3 个 flag。
awk 可开启 Lint 执行,会输出一些类似 DEBUG 的警告提示或错误提示:l--lint=invalid
。当进程所用到的 fd
超过 ulimit
的时候,标准错误会输出: 1
warning: reached system limit for open files: starting to multiplex file descriptors
源文件 io.c:1208-1210: 1
2
3
4if (do_lint && ! warned) {
warned = true;
lintwarn(_("reached system limit for open files: starting to multiplex file descriptors"));
}
这个与 awk info 里面所述一致,然后 Lint 会报 1
warning: unnecessary mixing of `>' and `>>' for file
这个警告提示,说明 awk 内部已经把 >
换成了 >>
在执行,在执行层面上来看符合猜想,具体代码参考 io.c:819-826
: 1
2
3
4
5
6
7case redirect_output:
outflag = (RED_FILE|RED_WRITE);
tflag |= outflag;
if (redirtype == redirect_output)
what = ">";
else
what = ">>";
至于 awk 如何判断这个文件是新打开的还是已有的文件,参考 gawkapi.c:1334-1423
的 api_get_file
函数。
最重要的就是解释如何「复用」数量有限的文件描述符(fd
),主要逻辑集中在 io.c
的 redirect_string
以及 close_one
函数。close_one
函数较为容易理解(io.c:1200-1235
),根据注释:临时关闭并打开文件进而复用文件描述符,具体逻辑为定位到「fd
使用热度链表」中最不常用的那个 fd
开始,依次往常用的 fd
遍历,如果能关闭则关闭之(当然像标准输入、标准输出及标准错误这三个 fd
是不能关闭的)。回到调用 close_one
函数的 redirect_string
函数(io.c:1070-1073
)后 931 行的 while 循环继续执行: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17while (rp->output.fp == NULL && rp->iop == NULL) {
if (! new_rp && (rp->flag & RED_EOF) != 0) {
save_rp = NULL;
return rp;
}
mode = NULL;
errno = 0;
switch (redirtype) {
case redirect_output:
mode = binmode("w");
if ((rp->flag & RED_USED) != 0)
mode = (rp->mode[1] == 'b') ? "ab" : "a";
break;
case redirect_append:
...
case redirect_pipe:
...
由于使用 print >
进行重定向,所以上述 switch(redirtype)
会走 redirect_output
分支,并重新使文件描述符与文件绑定(io.c:1026-1029): 1
2
3
4if (mode != NULL) {
errno = 0;
rp->output.mode = mode;
fd = (extfd >= 0) ? extfd : devopen(str, mode);
以上解释了 awk 如何在有限的系统 ulimit -n
设定下 print >
的表现如同 >>
且能正常处理文本。
附录
awk --lint=invalid
标准错误信息(节选):
1 | awk: cmd. line:1: (FILENAME=seq.txt FNR=97) warning: reached system limit for open files: starting to multiplex file descriptors |