OS X,bash:对打开的文件描述符的工作较少,cat 没有

Sig*_*uza 11 osx less bash file-descriptors cat

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

exec 9>&1
exec 5<>/tmp/some-file.txt
exec 1>&5
Run Code Online (Sandbox Code Playgroud)

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

cat /dev/fd/5
Run Code Online (Sandbox Code Playgroud)

或者

tee </dev/fd/5
Run Code Online (Sandbox Code Playgroud)

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

less /dev/fd/5 | tee
Run Code Online (Sandbox Code Playgroud)

但这似乎是一个黑客。

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

Sté*_*las 14

在 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),您将获得与 open on 相同的文件的全新打开文件描述x。您获得的新 fd 与 fd x 没有任何关系。特别是,偏移量将在文件的开头(除非你O_APPEND当然打开它)并且模式(读/写/追加...)可以与 fd x 上的不同(你甚至可以得到与 fd x 上的完全不同,例如以相反模式打开管道时的另一端)。(这也意味着这不适用于您无法open() 的套接字)。

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

exec 5<> file
echo test >&5
Run Code Online (Sandbox Code Playgroud)

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

cat <&5
Run Code Online (Sandbox Code Playgroud)

你什么也得不到。

当你这样做时:

cat /dev/fd/5
Run Code Online (Sandbox Code Playgroud)

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

在其他系统上,在

cat /dev/fd/5
Run Code Online (Sandbox Code Playgroud)

cat 得到一个 fd 是 fd 5 的副本,所以在文件末尾仍然有一个偏移量。

为什么它的工作原理与理由less是,由于某种原因,less做一个lseek()对FD的文件的开头(没有一个lseek(1); lseek(0)确定文件是否是可搜索的或没有)。

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

exec 5< file 9>&1 > file
Run Code Online (Sandbox Code Playgroud)

或者你必须重新打开文件,如果仍然存在,或者做lseek()同样的事情less

ksh93并且zsh是唯一具有内置lseek()运算符的外壳:

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

或者:

cat /dev/fd/5 5<#((0))  # ksh93
sysseek -u 5 0; cat /dev/fd/5 # zsh
Run Code Online (Sandbox Code Playgroud)