OS X、bash:较少对打开的文件描述符起作用,而 cat 则不然

OS X、bash:较少对打开的文件描述符起作用,而 cat 则不然

在我正在处理的 bash 脚本中(必须在 Ubuntu 和 OS X 上运行),我需要将数百个命令的输出重定向到一个文件。我只是简单地添加,
而不是附加&>...到所有这些

exec 9>&1
exec 5<>/tmp/some-file.txt
exec 1>&5

到目前为止一切顺利,但在所有这些命令的中间,我需要读取到目前为止已写入的所有内容,同时保持文件描述符打开。
现在,在 Ubuntu 上我可以简单地做

cat /dev/fd/5

或者

tee </dev/fd/5

但在 OS X 上,根本不打印任何内容(并且命令立即退出)。
但是,使用lessI 可以在两者上看到文件的内容。
我可以通过使用实现上述效果(在两种操作系统上工作)

less /dev/fd/5 | tee

但这似乎是一个黑客。

那么,为什么显然可以看到OS X 上看不到的less东西呢? cat(或者所有 BSD 后代都会受到影响吗?)
或者我做错了什么?

答案1

在 OS X 上,就像在所有支持它们的系统上一样Linux 除外,打开/dev/fd/x就像执行 a 一样dup(x),生成的 fd 或多或少指向与 fd x 相同的打开文件描述,特别是在文件内具有相同的偏移量。

Linux 在这里是个例外。在 Linux 上,/dev/fd/x是指向 fd x 上打开的文件的符号链接/proc/self/fd/x,也是/proc/self/fd/x伪符号链接。在 Linux 上,当你执行 a 时open("/dev/fd/x", somemode),你会得到一个全新的打开文件描述到与打开的文件相同的文件x。你获得的新fd与fd x没有任何关系。特别是,偏移量将位于文件的开头(当然,除非您使用它打开它O_APPEND),并且模式(读/写/追加...)可能与 fd x 上的模式不同(您甚至可以得到与 fd x 上的内容完全不同,例如以相反模式打开管道时的另一端)。 (这也意味着这不适用于套接字,例如您不能打开())。

所以,在 Linux 上,当你这样做时

exec 5<> file
echo test >&5

fd 5 的偏移量位于文件末尾。如果你这样做

cat <&5

你什么也得不到。

还是当你这样做时:

cat /dev/fd/5

您会看到test,因为获取了一个与 fd 5 无关的cat新只读 fd 。file

在其他系统上,根据

cat /dev/fd/5

cat获取一个与 fd 5 重复的 fd,因此在文件末尾仍然有一个偏移量。

它起作用的原因less是由于某种原因,在该 fd 上less执行 alseek()到文件的开头(执行 alseek(1); lseek(0)以确定文件是否可查找)。

在这里,如果您希望两者具有不同的偏移量,您可能希望有一个用于读取的 fd 和一个用于写入的 fd:

exec 5< file 9>&1 > file

或者,您必须重新打开该文件(如果仍然存在),或者执行lseek()同样的less操作。

ksh93zsh是唯一具有内置lseek()运算符的 shell:

cat <&5 <#((0)) # ksh93
{sysseek 0; cat} <&5 # zsh, zmodload zsh/system to enable that builtin

或者:

cat /dev/fd/5 5<#((0))  # ksh93
sysseek -u 5 0; cat /dev/fd/5 # zsh

相关内容