ファイル記述子を保存するには、別のfdに複製します。対応するファイルへのパスを保存するだけでは不十分です。オープニングモード、オープニングフラグ、ファイル内の現在の位置などを保存する必要があります。そしてもちろん、匿名のパイプまたはソケットの場合、パスがないため機能しません。保存したいのは、fdが参照するオープンファイルの説明です。fdを複製すると、実際には同じオープンファイルの説明に新しいfdが返されます。
Bourneのようなシェルを使用してファイル記述子を別の記述子に複製するには、構文は次のとおりです。
exec 3>&1
上記では、fd 1がfd 3に複製されます。
以前にfd 3が既に開いていたものはすべて閉じられますが、fds 3から9(通常はさらに最大99 yash
)がその目的のために予約されていることに注意してください(0、1、または2に反する特別な意味はありません)シェルは、独自の内部ビジネスにそれらを使用しないことを知っています。fd 3が前もって開いていた唯一の理由は、スクリプト1でそれをしたか、呼び出し元によってリークされたためです。
次に、stdoutを別のものに変更できます。
exec > /dev/null
その後、stdoutを復元します。
exec >&3 3>&-
(3>&-
不要になったファイル記述子を閉じることです)。
ここでの問題は、kshを除き、その後実行するすべてのコマンドがexec 3>&1
そのfd 3を継承することです。これはfdリークです。一般的には大したことではありませんが、それは問題を引き起こす可能性があります。
ksh
それらのfdsにclose-on-execフラグを設定します(2を超えるfdsの場合)が、他のシェルや他のシェルにはそのフラグを手動で設定する方法がありません。
他のシェルの回避策は、次のように、コマンドごとにfd 3を閉じることです。
exec 3>&-
exec > file.log
ls 3>&-
uname 3>&-
exec >&3 3>&-
面倒。ここで、最善の方法は、まったく使用せずexec
、コマンドグループをリダイレクトすることです。
{
ls
uname
} > file.log
そこでは、stdoutを保存して後で復元することに注意を払うのはシェルです(そして、fd(9を超える、99を超えるyash
)にclose-on-execフラグを設定して複製することで内部的に行います)。
注1
現在、これらのfds 3〜9の管理は、特にスクリプトでこれらのfdsを使用する可能性のあるサードパーティのコードを使用している場合、それらを広範囲にまたは関数で使用すると、面倒で問題が多くなります。
シェルによっては、( 、zsh
、bash
、ksh93
すべての機能(追加のオリバーKiddleによって提案されzsh
、この場合に役立ちます代わりに、10以上の最初のフリーFDを割り当てるための代替構文を持っている、それは彼らの開発者の間で議論された後、2005年に同時期に)):
myfunction() {
local fd
exec {fd}>&1
# stdout was duplicated onto a new fd above 10, whose actual value
# is stored in the fd variable
...
# it should even be safe to re-enter the function here
...
exec >&"$fd" {fd}>&-
}