相互接続コマンド間のデータ循環フローを実装するには?

相互接続コマンド間のデータ循環フローを実装するには?

コマンドの相互接続方法には2種類あります。

  1. パイプを使用して(標準出力を次のコマンドの標準入力に入れます)
  2. Teeを使用して(出力を複数の出力に接続)

これが可能かどうかわからないので、仮想接続タイプを描きました。

ここに画像の説明を入力してください。

コマンド間データの循環フローを実装する方法(たとえば、この擬似コードではコマンドの代わりに変数を使用します)

pseudo-code:

a = 1    # start condition 

repeat 
{
b = tripple(a)
c = sin(b) 
a = c + 1 
}

答え1

循環I/Oループの実装tail -f

これは循環I / Oループを実装します。

$ echo 1 >file
$ tail -f file | while read n; do echo $((n+1)); sleep 1; done | tee -a file
2
3
4
5
6
7
[..snip...]

これは、前述のサインアルゴリズムを使用してループ入力/出力ループを実装します。

$ echo 1 >file
$ tail -f file | while read n; do echo "1+s(3*$n)" | bc -l; sleep 1; done | tee -a file
1.14112000805986722210
.72194624281527439351
1.82812473159858353270
.28347272185896349481
1.75155632167982146959
[..snip...]

ここでは、bcの正弦関数表現である浮動bc小数点演算を実行します。s(...)

変数を使用した同じアルゴリズムの実装

この特定の数学例では、循環I / O方法は必要ありません。簡単に変数を更新できます。

$ n=1; while true; do n=$(echo "1+s(3*$n)" | bc -l); echo $n; sleep 1; done
1.14112000805986722210
.72194624281527439351
1.82812473159858353270
.28347272185896349481
[..snip...]

答え2

この目的のために作成したFIFOを使用できますmkfifo。しかし、その点は参考にしてください非常に誤ってデッドロックが発生しやすい。説明します。仮想の「ループ」を例に挙げてみましょう。コマンドの出力を入力に供給します。デッドロックが発生する方法は少なくとも2つあります。

  1. このコマンドには出力バッファがあります。部分的に塗りつぶされましたがフラッシュされませんでした(実際に書かれています)。いっぱいになるとこれが行われます。したがって、読み込み入力が返されます。待っている入力が実際に出力バッファにあるので、永遠にそこにいるはずです。入力を受け取るまでフラッシュされません。

  2. このコマンドには作成する出力がたくさんあります。書き込みは開始されますが、カーネルパイプバッファがいっぱいです。したがって、バッファー内のスペースが使用可能になるのを待ちます。入力を読み取った後は、出力に何も書き込みが終わるまでそのようなことをしないので決して起こらないということです。

つまり、実行方法は次のようになります。この例では、を使用してod無限の16進ダンプチェーンを作成します。

mkfifo fifo
( echo "we need enough to make it actually write a line out"; cat fifo ) \ 
    | stdbuf -i0 -o0 -- od -t x1 | tee fifo

最終的には停止します。なぜ?デッドロックです。上記の#2です。stdbufバッファリングを無効にする呼び出しが表示されることもあります。持っていませんか?デッドロック、出力なし。

答え3

ご存じのように、図で説明するように、繰り返しフィードバックループが必ずしも必要だとは思わない。持続性間のパイプ共同プロセス。そうすれば、あまりにも大きな違いはありません。 coprocessで行を開くと、通常のスタイルループを実装し、何もせずにここに情報を書き込んで情報を読み取ることができます。非常に珍しい点。

まず、bcコルーチンの主な候補のようです。擬似コードが必要とする機能とほぼ同じ機能を実行する関数をbc使用できます。defineたとえば、これを行う非常に単純な関数は次のとおりです。

printf '%s()\n' b c a |
3<&0 <&- bc -l <<\IN <&3
a=1; b=0; c=0;
define a(){ "a="; return (a = c+1); }
define b(){ "b="; return (b = 3*a); }
define c(){ "c="; return (c = s(b)); }
IN

...これが印刷されます...

b=3
c=.14112000805986722210
a=1.14112000805986722210

しかし、当然そうではありません。最後。パイプを担当するprintfサブシェルが終了するとprintfa()\nパイプに書いた直後)パイプが取り外され、bc入力が閉じて終了します。これは想像していたよりもずっと便利です。

@derobertはすでに言及しています。先入選出これは、次のように生成することで実行できます。名前付きパイプこのmkfifoユーティリティを使用してファイルを生成します。システムカーネルがファイルシステムエントリを両端に接続することを除いて、これらは本質的にパイプです。これは非常に便利ですが、ファイルシステムでスヌーピングのリスクなしにパイプのみを使用できる場合は、より良いでしょう。

結局のところ、シェルはこれを頻繁に行います。使用中のシェルが実装する場合プロセスの交換そうすれば、非常に簡単な方法で得ることができます一種の丈夫なネジパイプ - 通信可能なバックグラウンドプロセスに割り当てることができるタイプ。

たとえば、bashプロセス置換がどのように機能するかを確認できます。

bash -cx ': <(:)'
+ : /dev/fd/63

ご覧のとおり、これは実際には置換。シェルは拡張中にパスに対応する値を置き換えます。協会管路。これを活用できます。()交換自体で実行されているプロセスと通信するためにそのパイプを使用することに制限する必要はありません。

bash -c '
    eval "exec 3<>"<(:) "4<>"<(:)
    cat  <&4 >&3  &
    echo hey cat >&4
    read hiback  <&3
    echo "$hiback" here'

...印刷...

hey cat here

今私は他の殻ができることを知っています共同プロセスbashさまざまな方法で行います。設定のための特定の構文があります。(たぶん一つzsh- しかし、これがどのように機能するのかわかりません。私が知っているのは、bashandですべての退屈な作業を実行することなく、上記の構文を使用してほぼ同じことを実行できることです。そして、zshここのドキュメントを通して非常に似たようなことをし、同じことを達成できるということです。dashbusybox ash(ここでは、ドキュメントは他の2つのファイルと同様に一時ファイルの代わりにパイプを使用するためdashですbusybox

だから適用してみるとbc...

eval "exec 3<>"<(:) "4<>"<(:)
bc -l <<\INIT <&4 >&3 &
a=1; b=0; c=0;
define a(){ "a="; return (a = c+1); }
define b(){ "b="; return (b = 3*a); }
define c(){ "c="; return (c = s(b)); }
INIT
export BCOUT=3 BCIN=4 BCPID="$!"

…それが一番難しい部分です。ここに面白い部分があります...

set --
until [ "$#" -eq 10 ]
do    printf '%s()\n' b c a >&"$BCIN"
      set "$@" "$(head -n 3 <&"$BCOUT")"
done; printf %s\\n "$@"

...印刷...

b=3
c=.14112000805986722210
a=1.14112000805986722210
#...24 more lines...
b=3.92307618030433853649
c=-.70433330413228041035
a=.29566669586771958965

...まだ実行中です...

echo a >&"$BCIN"
read a <&"$BCOUT"
echo "$a"

bc...関数をa呼び出してsを増やして印刷するのではなく、sの最後の値を取得できます...a()

.29566669586771958965

実際、私はそれを殺してIPCパイプを引き裂くまで実行され続けます...

kill "$BCPID"; exec 3>&- 4>&-
unset BCPID BCIN BCOUT

答え4

通常、私はMakefile(makeコマンド)を使用してダイアグラムをmakefileルールにマップしたいと思います。

f1 f2 : f0
      command < f0 > f1 2>f2

反復/ループコマンドを使用するには、反復戦略を定義する必要があります。そして:

SHELL=/bin/bash

a.out : accumulator
    cat accumulator <(date) > a.out
    cp a.out accumulator

accumulator:
    touch accumulator     #initial value

それぞれがmake同時に反復を生成します。

関連情報