Bashコプロセス

Bash 4.0の新機能の1つはcoprocです。 coprocオペレーターを使用すると、2つのチャネルを使用してシェルに接続されたコプロセスを作成できます。1つはコプロセスにデータを送信し、もう1つはコプロセスから受信します。



リダイレクトexecを使用してログを書き込もうとすると、初めてこのためのアプリケーションが見つかりました。 目標は、オプションで、スクリプトの実行後にスクリプト出力をログファイルに書き込むことを許可することでした(たとえば、コマンドラインオプション--logによる )。



スクリプトの開始後に出力を記録する際の主な問題は、その出力が既に(ファイルまたはチャネルに)リダイレクトされる可能性があることです。 すでにリダイレクトされた出力をリダイレクトすると、ユーザーが意図したとおりにコマンドを実行できなくなります。



以前の実装は、名前付きパイプを使用して行われました。

#!/bin/bash echo hello if test -t 1; then # Stdout is a terminal. exec >log else # Stdout is not a terminal. npipe=/tmp/$$.tmp trap "rm -f $npipe" EXIT mknod $npipe p tee <$npipe log & exec 1>&- exec 1>$npipe fi echo goodbye
      
      





最後の記事から:

ここで、標準スクリプト出力ストリームが端末に接続されていない場合、 mknodを使用してパイプ (ファイルシステムにある名前付きパイプ )を作成し、スクリプトの終了時にtrapを使用して削除します。 次にteeを実行します。これは、バックグラウンドで入力ストリームを作成されたチャンネルに接続します。 ティーは 、入力ストリームから受け取ったすべてをファイルに書き込むこと加えて、すべてを標準出力ストリームにも出力することを忘れないでください。 また、 tee出力ストリームは、スクリプト(呼び出し元のteeスクリプト)の出力全体と同じ場所に向けられることに注意してください 。 したがって、 teeの出力は、指定されたスクリプトの出力ストリームが現在向けられている場所(つまり、ユーザーによってリダイレクトされた出力ストリーム、またはスクリプトがコマンドラインで呼び出されたときに指定されたパイプラインチャネル)に行きます。 したがって、必要な場所、つまりユーザーが定義したリダイレクトまたはパイプラインチャネルへの標準的なTシャツの出力が得られました。


コプロセスでも同じことができます。

 echo hello if test -t 1; then # Stdout is a terminal. exec >log else # Stdout is not a terminal. exec 7>&1 coproc tee log 1>&7 #echo Stdout of coproc: ${COPROC[0]} >&2 #echo Stdin of coproc: ${COPROC[1]} >&2 #ls -la /proc/$$/fd exec 7>&- exec 7>&${COPROC[1]}- exec 1>&7- eval "exec ${COPROC[0]}>&-" #ls -la /proc/$$/fd fi echo goodbye echo error >&2
      
      





標準出力が端末に送られる場合は、 execを使用して出力を目的のログファイルにリダイレクトするだけです。 出力が端末デバイスに接続されていない場合、coprocを使用してteeをコプロセスとして開始し、出力をtee入力にリダイレクトし、 tee出力を元々計画されていた場所にリダイレクトします。

coprocを使用してteeを実行すると、基本的にバックグラウンドでteeと同じように動作します(たとえば、 tee log& )。主な違いは、両方のチャネル(入力および出力)が接続されていることです。 デフォルトでは、 Bashはこれらのチャネルのファイル記述子をCOPROC配列に配置します。



チームでリダイレクトを使用する前に、これらのチャネルを作成する必要があることに注意してください。

スクリプト出力が端末に接続されていないスクリプトの部分に注意してください。 次の行は、標準出力をファイル記述子7に複製します。

 exec 7>&1
      
      





次に、出力をファイル記述子7にリダイレクトすることで、 teeを開始します。

 coproc tee log 1>&7
      
      





これで、 teeは標準入力から読み取ったすべてを、現在の標準出力であるlog and file descriptor 7というファイルに書き込みます。

ここで、ファイル記述子7を閉じます( teeも「ファイル」であり、7で標準出力として開かれていることを思い出してください)。

 exec 7>&-
      
      





7を閉じたので、再利用できるため、入力7のteeに接続されたチャネルを転送します。

 exec 7>&${COPROC[1]}-
      
      





次に、標準出力を標準入力Tに接続されたチャネル(ファイル記述子7)に移動します。

 exec 1>&7-
      
      





そして最後に、 tee出力に接続されたチャネルを閉じます。

 eval "exec ${COPROC[0]}>&-"
      
      





この場合、ここではevalが必要です。それ以外の場合、 Bash$ {COPROC [0]}の値がコマンドであると見なします。 一方、これは上記では必要ありません( exec 7>&$ {COPROC [1]}- )。これは、 Bashが「7」がファイル記述子で作業を開始し、コマンドと見なされないことを知っているためです。

コメントアウトされた行にも注意してください:

 #ls -la /proc/$$/fd
      
      





これは、現在のプロセスによって開かれたファイルを表示するのに便利です。



これで目的の効果が得られました。標準出力がteeに送信されます。 Teeにはログファイルへの「入力」があり、レコードは元々計画されていたチャネルとファイルの両方に送られます。

これまでのところ、コプロセス用のタスクは考えられません。少なくとも先取りされていません。 コプロセスの詳細については、 bashのマニュアルページを参照してください。



All Articles