web-dev-qa-db-ja.com

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では、何も出力されません(コマンドはすぐに終了します)。
ただし、lessを使用すると、両方のファイルの内容を確認できます。
上記の効果(両方のOSで動作)を使用することで達成できます

less /dev/fd/5 | tee

しかし、それはハックのようです。

では、なぜlessはOSXではcatが見ることができないものを明らかに見ることができるのでしょうか。 (または、すべてのBSD子孫が影響を受けますか?)
それとも私は何か間違ったことをしていますか?

10
Siguza

OS Xでは、サポートされているすべてのシステムと同様にLinuxを除く/dev/fd/xを開くことは、dup(x)を実行することと同じであり、結果のfdは多かれ少なかれ同じオープンを指します。 fd xと同様のファイル記述、特にファイル内で同じオフセットがあります。

Linuxはここでは例外です。 Linuxでは、/dev/fd/x/proc/self/fd/xへのシンボリックリンクであり、/proc/self/fd/xはfdxで開いているファイルへの疑似シンボリックリンクです。 Linuxでopen("/dev/fd/x", somemode)を実行すると、xで開くのと同じファイルにまったく新しいファイルの説明を開くが表示されます。取得した新しいfdは、fdxとはまったく関係ありません。特に、オフセットはファイルの先頭にあり(もちろん、O_APPENDで開く場合を除く)、モード(読み取り/書き込み/追加...)はfdxのものとは異なる場合があります。 (反対のモードでパイプを開いたときのパイプのもう一方の端のように、fd xにあるものとはまったく異なるものを取得することもできます)。 (これは、たとえば、できないソケットでは機能しないことも意味しますopen())。

したがって、Linuxでは、

exec 5<> file
echo test >&5

Fd5のオフセットはファイルの最後にあります。もしあなたがそうするなら

cat <&5

何も得られません。

それでもあなたがするとき:

cat /dev/fd/5

testがfd5とは関係のないcatへの新しい読み取り専用fdを取得するため、fileが表示されます。

他のシステムでは、

cat /dev/fd/5

catは、fd 5の複製であるfdを取得するため、ファイルの最後にオフセットがあります。

lessで機能する理由は、何らかの理由で、lessがファイルの先頭までのfdでlseek()を実行する(lseek(1); lseek(0)を実行する)ためです。ファイルがシーク可能かどうかを判断するため)。

ここで、両方に異なるオフセットを持たせたい場合は、読み取り用と書き込み用のfdが必要になる可能性があります。

exec 5< file 9>&1 > file

または、ファイルがまだある場合はファイルを再度開くか、lessのようにlseek()を実行する必要があります。

ksh93zshは、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
13