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

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

/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ディストリビューション間の移植性に制限されている)方法はありますか?

答え1

ファイル記述子を保存するには、別のファイル記述子にコピーしてください。そのファイルのパスを保存するだけでは十分ではありません。オープンモード、オープンフラグ、ファイルの現在位置などを保存する必要があります。もちろん、匿名パイプやソケットにはパスがないため、これは機能しません。保存したいことファイル説明を開くfdが参照したようにfdをコピーすると、実際には同じfdに新しいfdが返されます。ファイル説明を開く

ファイル記述子を別のファイル記述子にコピーするには、Bourneに似たシェルを次の構文で使用します。

exec 3>&1

上記では、fd 1がfd 3にコピーされました。

以前に開いていたfd 3は何でも閉じますが、fd 3から9まで(通常はそれ以上、最大99までyash)はこの目的のために予約されています(0、1、または2とは異なり、特別な意味はありません)、わかりません。内部業務に使用します。 fd 3が早く開かれる唯一の理由は、スクリプト1で実行されたか、呼び出し元によって漏洩したためです。

その後、stdoutを別のものに変更できます。

exec > /dev/null

その後、標準出力を復元します。

exec >&3 3>&-

3>&-不要になったファイル記述子を閉じます)。

今の問題は、kshを除いて、それ以降に実行されるすべてのコマンドがexec 3>&1fd 3を継承することです。 fd流出です。一般的に大きな問題ではありませんが、問題が発生する可能性があります。

ksh設定実行時に閉じるこれらのfd(2つ以上のfdの場合)にはフラグがありますが、他のシェルにはフラグがなく、他のシェルでフラグを手動で設定する方法はありません。

他のシェルの回避策は、各コマンドに対してfd 3を閉じることです。たとえば、次のようになります。

exec 3>&-

exec > file.log

ls 3>&-
uname 3>&-

exec >&3 3>&-

問題。ここで最善の方法は、execまったく使用せずにコマンドグループをリダイレクトすることです。

{
  ls
  uname
} > file.log

そこで、シェルはstdoutを保存して後で復元することを担当します(yash内部的にfdにコピーしてこれを行います(99以上の場合)。実行時に閉じるフラグ設定)。

1

これで、これらのfd 3〜9を広範囲に使用するか、関数内で使用している場合、管理が面倒で問題になる可能性があります。特に、スクリプトがこれらのfdを使用できるいくつかのサードパーティのコードを使用している場合は、さらにそうです。

一部のシェル(、、、、zshこの機能を追加しました(bashksh93Oliver Kiddleが提案したものzsh)2005年の同様の時期に、開発者間の議論の終わりに)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}>&-
}

答え2

$$対話型シェルの場合、または関連シェル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

答え3

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

最も簡単な解決策は、サブシェルを使用してリダイレクトしたいエントリを実行して、フル標準I / Oを介して処理を最上位シェルに復元できるようにすることです。

別の解決策は、ttyTTYデバイスを識別し、スクリプトからI / Oを制御することです。たとえば、

dev=$(tty)

それからあなたはできます...

echo message > $dev

関連情報