web-dev-qa-db-ja.com

/ dev / stdoutのターゲットの場所をbashスクリプトに保存する方法は?

最初のファイル記述子を他の場所に置き換える前に、元の_/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ディストリビューション間の移植性を対象とした)方法はありますか?

12
alexey.e.egorov

ファイル記述子を保存するには、別の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フラグセット)。

注意 1

現在、これらのfds 3〜9の管理は、それらを広範囲に使用する場合、または関数で使用する場合、特にスクリプトがそれらのfdsを使用する可能性のあるサードパーティのコードを使用する場合、面倒で問題になる可能性があります。

一部のシェル(zshbashksh93、追加されたすべての機能( 開発者の間で議論された後、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}>&-
}
14

ご覧のとおり、bashスクリプトは、ファイル記述子を割り当てることができる通常のプログラミング言語とは異なります。

最も簡単な解決策は、サブシェルを使用してリダイレクトしたいものを実行し、処理を標準I/Oがそのままのトップシェルに戻すことができるようにすることです。

別の解決策は、ttyを使用してTTYデバイスを識別し、スクリプトのI/Oを制御することです。例えば:

dev=$(tty)

そして、あなたはすることができます。

echo message > $dev
3
Julie Pelletier

$$インタラクティブシェルの場合、または関連するシェル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
1
heemayl