多くの
Bashユーザーは、バージョン
4のBashに登場するコプロセスの存在を認識しています。
Bashのコ
プロセスが新しい機能ではなく、1988年に
ksh88の実装に登場した古代の
KornShell機能である
ことは、わずかに少ない数字でわかっています。 コプロセスの方法を知っているさらに少数のシェルユーザーは、構文を知っており、その方法を覚えています。 おそらく、私は4番目のグループに属します。コプロセスについては知っていて、定期的に使用方法を知っているが、「なぜ?」 「定期的に」と言うのは、時々頭の中で構文を更新するからですが、「これがco-procを使用できる場合」と思う頃には、その方法を完全に忘れています。
このメモでは、さまざまなシェルの構文をまとめて、必要な理由を理解できるように、どうすればよいか覚えていない場合は、少なくともどこに書かれているかを理解できるようにします。
記事のタイトルには3つの質問があります。 順番に行きましょう。
なに?
コプロセスとは何ですか? コプロセッシングとは、2つのプロシージャを同時に実行することで、一方のプロシージャが他方の出力を読み取ります。 これを実装するには、最初に
チャネル機能を実行する
バックグラウンドプロセスを実行する必要があります。 バックグラウンドプロセスが開始されると、その
stdinと
stdoutはユーザープロセスに関連付けられたチャネルに割り当てられます。 したがって、書き込み用の1つのチャネル、読み取り用の2番目のチャネル。 これは例で説明する方が簡単なので、2番目の質問に移りましょう。
どうやって?
シェルでのコプロセスの実装はさまざまです。
ksh 、
zsh 、および
bashで知っている3つの実装に焦点を当てます。 それらを時系列順に検討してください。 これは記事の問題に直接関連するものではありませんが、以下のすべての例は
$ uname -opr FreeBSD 10.1-STABLE amd64
Ksh $ `echo $0` --version version sh (AT&T Research) 93u+ 2012-08-01
構文
cmd |&
それは私にとって最も論理的なようです。 ここで、
cmdコマンドをバックグラウンドで実行するには、特別な操作
|&を使用します。
-「&」-バックグラウンドプロセス。
-「|」 -チャンネル。
バックグラウンドプロセスを開始します。
$ tr -uab |& [2] 6053
彼が生きていることを確認してください:
$ ps afx | grep [6]053 6053 4 IN 0:00.00 tr -uab
UPD 2016.08.15 21:15このコンテキストでは、まったく正しいコマンドではありません。 コメントの論理に違反しないように訂正しません。
このようなより正しい:
$ tr -uab |& [2] 6053 $ ps -p 6053 PID TT STAT TIME COMMAND 6053 4 SN 0:00.00 tr -uab
ありがとう
ZyXI これで、バックグラウンドプロセスと通信できます。 私たちは書きます:
$ print -p abrakadabra1 $ print -p abrakadabra2 $ print -p abrakadabra3
そして読む:
$ read -p var; echo $var bbrbkbdbbrb1 $ read -p var; echo $var bbrbkbdbbrb2 $ read -p var; echo $var bbrbkbdbbrb3
または:
$ print abrakadabra1 >&p $ print abrakadabra2 >&p $ print abrakadabra3 >&p $ while read -p var; do echo $var; done bbrbkbdbbrb1 bbrbkbdbbrb2 bbrbkbdbbrb3
記録のためにパイプの「終了」を閉じます。
$ exec 3>&p 3>&-
そして読むために:
$ exec 3<&p 3<&-
Zsh $ `echo $0` --version zsh 5.2 (amd64-portbld-freebsd10.1)
zshのコプロセスの構文は
kshとあまり変わらないので、驚くことではありません。 彼の男は
「zshはkshに最も似ている
」と言っています。
主な違いは、
|&演算子の代わりに
coprocキーワードを使用することです。 そうでなければ、すべてが非常に似ています:
$ coproc tr -uab [1] 22810 $ print -p abrakadabra1 $ print abrakadabra2 >&p $ print -p abrakadabra3 $ read -ep bbrbkbdbbrb1 $ while read -p var; do echo $var; done bbrbkbdbbrb2 bbrbkbdbbrb3
読み取り/書き込みチャネルを閉じるには、
終了イディオムを使用できます。
$ coproc exit [1] 23240 $ [2] - done tr -uab $ [1] + done exit
同時に、新しいバックグラウンドプロセスが開始され、すぐに終了しました。 これは
kshとは別の違いです。既存のコプロセスを閉じることはできませんが、すぐに新しいコプロセスを開始できます。
$ coproc tr -uab [1] 24981 $ print -p aaaaa $ read -ep bbbbb $ coproc tr -uad [2] 24982 $ [1] - done tr -uab $ print -p aaaaa $ read -ep ddddd $
kshでは、次のようになります。
$ tr -uab |& [1] 25072 $ tr -uad |& ksh93: process already exists
この機能にもかかわらず、特に
"setopt NO_HUP"を使用する場合は、常に明示的にバックグラウンドプロセスを
強制終了することをお勧めします。
ここで言及する価値があるのは、出力バッファリングに関連する予期しない結果が得られる場合があることです。そのため、上記の例では
-uオプションを指定して
trを使用しています。
$ man tr | col | grep "\-u" -u Guarantee that any output is unbuffered.
これはコプロセスのみを指すものではありませんが、例を使用してこの動作を示します。
$ coproc tr ab [1] 26257 $ print -pa $ read -ep ^C $ [1] + broken pipe tr ab
バッファがいっぱいではなく、パイプから何も取得しません。 「上に」記入してください:
$ coproc tr ab [1] 26140 $ for ((a=1; a <= 4096 ; a++)) do print -p 'a'; done $ read -ep b
もちろん、この動作が私たちに合わない場合は、たとえば
stdbufを使用して変更できます
$ coproc stdbuf -oL -i0 tr ab [1] 30001 $ print -pa $ read -ep b
バッシュ $ `echo $0` --version GNU bash, version 4.3.42(1)-release (amd64-portbld-freebsd10.1)
bashワード
coprocは 、
bshおよび
zshでコプロセスを開始するために使用され
ますが 、上記のシェルとは異なり、コプロセスは
>&pおよび
<&pを使用してアクセスされず、
$ COPROC配列を介してアクセスされます。
-レコードの
$ {COPROC [0]} 。
-読むべき
$ {COPROC [1]} 。
したがって、書き込み/読み取り手順は次のようになります。
$ coproc tr -uab [1] 30131 $ echo abrakadabra1 >&${COPROC[1]} $ echo abrakadabra2 >&${COPROC[1]} $ echo abrakadabra3 >&${COPROC[1]} $ while read -u ${COPROC[0]}; do printf "%s\n" "$REPLY"; done bbrbkbdbbrb1 bbrbkbdbbrb2 bbrbkbdbbrb3
ハンドルを閉じる:
$ exec {COPROC[1]}>&- $ cat <&"${COPROC[0]}" [1]+ Done coproc COPROC tr -uab
何らかの理由で
COPROCという名前が
自分に合わない場合は、
独自の名前を指定できます。
$ coproc MYNAME (tr -uab) [1] 30528 $ echo abrakadabra1 >&${MYNAME[1]} $ read -u ${MYNAME[0]} ; echo $REPLY bbrbkbdbbrb1 $ exec {MYNAME[1]}>&- ; cat <&"${MYNAME[0]}" [1]+ Done coproc MYNAME ( tr -uab )
なんで?
なぜコプロセスが必要なのかを答える前に、すぐにコプロセスを持たないシェルでそれらの機能を実装できるかどうかを考えます。 たとえば、次の場合:
$ man sh | col -b | grep -A 4 DESCRIPTION DESCRIPTION The sh utility is the standard command interpreter for the system. The current version of sh is close to the IEEE Std 1003.1 (``POSIX.1'') spec- ification for the shell. It only supports features designated by POSIX, plus a few Berkeley extensions. $ man sh | col -b | grep -A 1 -B 3 AUTHORS This version of sh was rewritten in 1989 under the BSD license after the Bourne shell from AT&T System V Release 4 UNIX. AUTHORS This version of sh was originally written by Kenneth Almquist.
誰も名前付きパイプをキャンセルしませんでした:
$ mkfifo in out $ tr -uab <in >out & $ exec 3> in 4< out $ echo abrakadabra1 >&3 $ echo abrakadabra2 >&3 $ echo abrakadabra3 >&3 $ read var <&4 ; echo $var bbrbkbdbbrb1 $ read var <&4 ; echo $var bbrbkbdbbrb2 $ read var <&4 ; echo $var bbrbkbdbbrb3
おそらく、いくらか誇張して、疑似端末を使用してこれが可能であると言うことができますが、このトピックの開発は始めません。
さて、なぜコプロセスが必要なのですか?
Mitch Frazierによる記事の翻訳からの抜粋を引用します。
これまでのところ、コプロセス用の<...>タスクを思いつくことはできません。少なくとも大げさではありません。
実際、私はスクリプトでコプロセスを使用できるのは一度だけでした。 アイデアは、MySQLにアクセスするための「永続的な接続」を実装することでした。
次のようになりました。
$ coproc stdbuf -oL -i0 mysql -pPASS [1] 19743 $ printf '%s;\n' 'select NOW()' >&${COPROC[1]} $ while read -u ${COPROC[0]}; do printf "%s\n" "$REPLY"; done NOW() 2016-04-06 13:29:57
それ以外の場合、
coprocを使用しようとするすべての試みは、本当に遠いものでした。
ありがとうコメント、手紙、メモが記事の執筆に役立ったバートシェーファー、ステファンシャゼラス、ミッチフレイザーに感謝します。