文件描述符复制行为

iru*_*var 3 linux bash file-descriptors

我有这个文件:

1
2
3
4
Run Code Online (Sandbox Code Playgroud)

当我paste以这种方式奔跑时

paste - - <file
Run Code Online (Sandbox Code Playgroud)

这结果

1       2
3       4
Run Code Online (Sandbox Code Playgroud)

到目前为止这么好,标准输入(重定向自file)被传递两次,paste因此奇数和偶数行成对打印。

但是,当这更改为

paste - /dev/fd/0 <file
Run Code Online (Sandbox Code Playgroud)

输出变成

1       1
2       2
3       3
4       4
Run Code Online (Sandbox Code Playgroud)

paste似乎打开了file两次(导致内核文件表中有两个不同的条目?),一次用于-参数,一次用于/dev/fd/0.

我茫然地解释这一点-如果我解释这个正确的,既-/dev/fd/0应该被引用,因此单内核文件表项paste应该得到相同的结果与前面的情况。

Sté*_*las 5

这是 Linux 特有的。虽然在大多数 Unices 上,opening/dev/fd/n或多或少与dup(n)(获取与fd 上相同的打开文件描述的文件描述符n)相同,但在 Linux 上,/dev/fd/n是文件描述符上打开的文件的符号链接n

所以,在 Linux 上:

paste - /dev/fd/0 < file
Run Code Online (Sandbox Code Playgroud)

是相同的:

paste - file < file
Run Code Online (Sandbox Code Playgroud)

(或paste file file)。

两个 fds(0 代表-和通过打开/dev/fd/0或获得自己的file)是独立的,并且在文件中有自己的光标。

您还会注意到,在 Linux 上,您不能将 /dev/fd/n 与套接字一起使用。

通常在 Linux 上,您只想/dev/fd/n与管道一起使用。但在这种情况下

cat file | paste - /dev/fd/0
Run Code Online (Sandbox Code Playgroud)

(或切换到非基于 Linux 的操作系统)不会真正有帮助。paste - -有效,因为paste在这两种情况下都知道它是标准输入。但不是在这里,所以它会从-(fd 0 到管道)读取整个块(而不是一行),然后从 /dev/fd/0读取另一个块(Linux 上同一管道的独立 fd,但是这些 fds 是否指向相同的打开文件描述对于管道来说并不重要)。两个read()s 都将读取文件的不同部分,但一次读取几行。

您需要告诉一次paste读取一个字节,这样在从-读取一行之前它不会读取多于一行,/dev/fd/0如果不重新编译,您可能无法做到这一点。您可能可以paste使用 一次读取一个字节的标准输入stdbuf,但可能不是/dev/fd/0

$ cat file | paste - /dev/fd/0
1
2
3
4
$ cat file | stdbuf -i1 paste - /dev/fd/0
1       2
        3
        4
Run Code Online (Sandbox Code Playgroud)