在我正在处理的 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 上,根本不打印任何内容(并且命令立即退出)。
但是,使用less
I 可以在两者上看到文件的内容。
我可以通过使用实现上述效果(在两种操作系统上工作)
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
操作。
ksh93
和zsh
是唯一具有内置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