最初のファイル記述子を他の場所に置き換える前に、元の_/dev/stdout
_の場所を保持したい特定のbashスクリプトがあります。
だから、当然、私は次のようなものを書きました
_old_stdout=$(readlink -f /dev/stdout)
_
そしてそれはうまくいきませんでした。すぐに私は問題が何であったかを理解します:
_test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18
_
明らかに、$()
は、親シェルにパイプされているサブシェルで実行されます。
したがって、問題は、_/dev/stdout
_の場所をbashスクリプトの文字列として保存するための信頼できる(Linuxディストリビューション間の移植性を対象とした)方法はありますか?
ファイル記述子を保存するには、別のfdに複製します。対応するファイルへのパスを保存するだけでは不十分です。開始モード、開始フラグ、ファイル内の現在の位置などを保存する必要があります。そしてもちろん、無名パイプまたはソケットの場合、パスがないため機能しません。保存したいのは、fdが参照するopen file descriptionであり、fdを複製すると、実際には同じopenfileに新しいfdが返されます。説明。
Bourne-like Shellを使用してファイル記述子を別の記述子に複製するには、構文は次のとおりです。
exec 3>&1
上記では、fd1がfd3に複製されています。
Fd 3が以前に開いていたものはすべて閉じられますが、fds 3から9(通常はそれ以上、yash
で最大99)はその目的のために予約されていることに注意してください(0、1とは反対の特別な意味はありません)。 、または2)、シェルはそれらを自社の内部ビジネスに使用しないことを知っています。 fd 3が事前に開いていた唯一の理由は、スクリプトでそれを行ったためです。1、または発信者によってリークされました。
次に、stdoutを別のものに変更できます。
exec > /dev/null
そして後で、stdoutを復元するには:
exec >&3 3>&-
(3>&-
不要になったファイル記述子を閉じること)。
さて、それに関する問題は、kshを除いて、その後に実行するすべてのコマンドがexec 3>&1
はそのfd3を継承します。これはfdリークです。一般的に大したことではありませんが、それは問題を引き起こす可能性があります。
ksh
はそれらのfdsにclose-on-execフラグを設定しますが(2を超えるfdsの場合)、他のシェルや他のシェルにはそのフラグを手動で設定します。
他のシェルの回避策は、次のように、すべてのコマンドのfd3を閉じることです。
exec 3>&-
exec > file.log
ls 3>&-
uname 3>&-
exec >&3 3>&-
面倒。ここでの最善の方法は、exec
をまったく使用せず、コマンドグループをリダイレクトすることです。
{
ls
uname
} > file.log
そこで、stdoutを保存し、後で復元するように注意するのはシェルです(そして、fd(9より上、yash
の場合は99より上)にclose-on-execフラグセット)。
現在、これらのfds 3〜9の管理は、それらを広範囲に使用する場合、または関数で使用する場合、特にスクリプトがそれらのfdsを使用する可能性のあるサードパーティのコードを使用する場合、面倒で問題になる可能性があります。
一部のシェル(zsh
、bash
、ksh93
、追加されたすべての機能( 開発者の間で議論された後、2005年のほぼ同時期にzsh
のOliver Kiddleによって提案されました)最初の無料を割り当てるための代替構文があります代わりに10を超えるfdは、この場合に役立ちます。
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}>&-
}
ご覧のとおり、bashスクリプトは、ファイル記述子を割り当てることができる通常のプログラミング言語とは異なります。
最も簡単な解決策は、サブシェルを使用してリダイレクトしたいものを実行し、処理を標準I/Oがそのままのトップシェルに戻すことができるようにすることです。
別の解決策は、tty
を使用してTTYデバイスを識別し、スクリプトのI/Oを制御することです。例えば:
dev=$(tty)
そして、あなたはすることができます。
echo message > $dev
$$
インタラクティブシェルの場合、または関連するシェルPIDをスクリプト化する場合、現在のプロセスPIDを取得します。
したがって、次を使用できます。
readlink -f /proc/$$/fd/1
例:
% readlink -f /proc/$$/fd/1
/dev/pts/33
% var=$(readlink -f /proc/$$/fd/1)
% echo $var
/dev/pts/33