Bash

OS X, bash: less 對打開的文件描述符起作用,cat 沒有

  • June 5, 2018

在我正在處理的 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我可以在兩者上看到文件的內容。

我可以通過使用實現上述效果(在兩個作業系統上工作)

less /dev/fd/5 | tee

但這似乎是一個黑客行為。

那麼,為什麼less顯然可以看到cat在 OS X 上看不到的東西呢?(或者所有 BSD 後代都受到影響?)

還是我做錯了什麼?

在 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 on 相同的文件的open("/dev/fd/x", somemode)全新打開文件描述x。您獲得的新 fd 與 fd x 沒有任何關係。特別是,偏移量將在文件的開頭(O_APPEND當然,除非你用它打開它)並且模式(讀/寫/追加…)可能與 fd x 上的不同(你甚至可以得到與 fd x 上的完全不同的東西,比如在相反模式下打開管道的另一端)。(這也意味著這不適用於您無法*open()*的套接字)。

所以,在 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 上執行 a 到文件的開頭(執行 aless以確定文件是否可查找)。lseek()``lseek(1); lseek(0)

在這裡,如果您希望兩者都具有不同的偏移量,您可能希望有一個用於讀取的 fd 和一個用於寫入的 fd:

exec 5< file 9>&1 > file

或者,如果文件仍然存在,您將不得不重新打開該文件,或者按lseek()原樣less執行。

ksh93並且zsh是唯一具有內置lseek()運算符的外殼:

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

引用自:https://unix.stackexchange.com/questions/209620