答え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つあります。
このコマンドには出力バッファがあります。部分的に塗りつぶされましたがフラッシュされませんでした(実際に書かれています)。いっぱいになるとこれが行われます。したがって、読み込み入力が返されます。待っている入力が実際に出力バッファにあるので、永遠にそこにいるはずです。入力を受け取るまでフラッシュされません。
このコマンドには作成する出力がたくさんあります。書き込みは開始されますが、カーネルパイプバッファがいっぱいです。したがって、バッファー内のスペースが使用可能になるのを待ちます。入力を読み取った後は、出力に何も書き込みが終わるまでそのようなことをしないので決して起こらないということです。
つまり、実行方法は次のようになります。この例では、を使用して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
サブシェルが終了するとprintf
(a()\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
)- しかし、これがどのように機能するのかわかりません。私が知っているのは、bash
andですべての退屈な作業を実行することなく、上記の構文を使用してほぼ同じことを実行できることです。そして、zsh
ここのドキュメントを通して非常に似たようなことをし、同じことを達成できるということです。dash
busybox 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
同時に反復を生成します。