stdoutへの出力は機能しますが、ファイルまたはfifoにリダイレクトされると失敗します。

stdoutへの出力は機能しますが、ファイルまたはfifoにリダイレクトされると失敗します。

Wi-Fiアンテナがありますが、その位置を最適化したいです。この提案では、現在の待ち時間をリアルタイムで視覚化したいと思います。

ping待ち時間を出力するために1行のスクリプトを作成しました。

緯度:

ping 8.8.8.8|gawk '/64 bytes/{ match($7,/[0-9.]+/,arr); print((i++),"  ",arr[0]);}'

gnuplotを使ってプロットしたい。

plot "<lat.sh"

それは動作しません。 lat.shが端末に表示すべき内容を表示することがわかりました。たとえば、次のようになります。

$ ./lat.sh 
0    47.7
1    25.5
2    15.8
3    16.7

ただし、パイプへの出力は機能しません。./lat.sh > outfileまたは、端末には何も印刷されませんが、ファイル(またはパイプ)は空です./lat.sh|tee outfile./lat.sh &> outfilegnuplotパイプラインも同様です。本当に混乱しています。

答え1

マクセルフのように説明する以下はバッファリングの問題です。この問題を解決する1つの方法は、awkすぐに使用を強制することですfflush()(残りは現在実行中の作業を単純化したバージョンです)。

ping 8.8.8.8 | gawk -F'[= ]' '/^64/{print i++,$(NF-1); fflush()}'

この呼び出しは、バッファがいっぱいになるのを待たずに出力が可能になるとすぐに出力されるようfflush()にします。awk

あるいは、ping特定の回数だけ実行してみることもできます。たとえば、10:

ping  -c 10 8.8.8.8 | gawk ...

答え2

問題はここにあるようですawk。プログラムから出力が省略されました。切り取ってください。これは非常に便利ですが、これは待機中の出力を減らしますawk。ほとんどのUNIXプログラムではstdio.h (これはほとんどのUNIXプログラムです)出力デバイスの出力は、そのタイプに応じてバッファリングされます。出力デバイスが端末の場合、ほとんどのUNIXプログラムは出力をラインバッファリングし、1行write()に1回実行します。いついいえしかし、(パイプの時のように)プログラムは、書き込みごとに定義されたバイト数をバッファリングします。

これは通常標準的な動作です。いいねもの。すべての呼び出しwrite()はシステム呼び出しです。これは、システムカーネルのコア機能と最も基本的な機能への接続です。 Unixシステムは時間共有- カーネルは必要に応じて各要求プロセスに CPU 処理時間を割り当てます。だから支払う効率を上げ、I/O を管理可能な塊に分割します。これがアプリケーションがバッファリングされる理由です。

これがawk私たちがやっていることです。問題は、入力フィルタリングによって自己出力が毎秒約8〜10バイトに減少するという事実によってさらに複雑になります。awk出力が4kbチャンクでバッファリングされている場合(これは一般的なことです)、毎秒約10バイトの出力のみが生成されるため、6分に1回だけ書き込まれます。大陸ドリフトスケールの特定の周波数を調べない限り、遅延を測定するのにあまり役に立たないアプリケーションではありません。

この問題を処理する方法はいくつかあります。たとえば、パイプをptyで包むことができます。screenこれはユーザーや他の多くのアプリケーションで行うことができます。これを処理する-nativeオプションがあるかもしれませんが、awkわかりません。(でもテデンは必ずしもそうだろう)。 GNU ツールは、stdbufプログラムの実行中に libc 呼び出しを注入して出力バッファを設定できます。これは通常非常にうまく機能しますが、後で実行されるプログラムが実行中にバッファを調整する場合にはあまり役に立ちません。

awk今は非難していますが、ping出力をバッファリングすることも可能です。しかし、少なくとも私のLinuxシステムでは、次のように呼び出します。

ping -O -n 8.8.8.8 | ...

...そうではありません。質問がある場合は、標準のUNIXコマンドに対して非常に基本的なPOSIXオプションのみを使用して、利用可能なすべての出力行を一貫して作成するようにしました。

最も簡単:

ping -On 8.8.8.8 |
sed -u 's/^64.*=\(.*\) ttl.*=/\1  /' |
more pipeline

... GNU、BSD、またはASTで使用されますsed。しかも…

ping -On 8.8.8.8 |
sed 's/^64.*=\(.*\) ttl.*=/\1  /w target_file' |
more pipeline

...可能な処理の後、すべての入力行を標準出力にコピーします。これはブロックバッファリングされる可能性が高いです。しかしそれは返品w各成功した交換結果を直ちに次の場所に記録します。s///宛先ファイルw指定されたすべての意識ファイルに対する即時の意識は、wPOSIXで指定されたsed動作です。(ほぼ)readファイルも同じです。テーマを少し変えると次のようになります。

ping -On 8.8.8.8 |
sed 's/^64.*=\(.*\) ttl.*=/\1  /w /dev/fd/1' |
more pipeline

リンクアドレス付きファイル記述子の使用をサポートするシステムでは、上記は/dev少なくとも現在では標準シェルパイプを介してラインバッファ出力を生成する必要がありますsed。これは、直接接続をサポートしていないシステム、さらには古いシステムでも依然として広く使用されている処理機能/dev/fdです。デフォルトのstdout出力を完全に無効にするために使用されます。sed/dev/std(in|out|err)minised-n

以下は、この構成に基づくより柔軟で複雑なソリューションです。

mkfifo  /tmp/ipipe    /tmp/opipe
exec 9<>/tmp/opipe 8<>/tmp/ipipe
trap '  printf \\nTTY:STOP\\n >&8' INT
sed -n 's/^64.*=\(.*\) ttl.*=/\1\t/
        /^TTY:START$/,/^TTY:STOP$/{
                /^TTY/d;w /dev/tty
};      /^PIP:START$/,/^PIP:STOP$/{
                /^PIP/d;w /tmp/opipe
}'      <&8     >/dev/null 2>&1 & SEDPID=$!
ping -On 8.8.8.8       >&8 2>&8 & PNGPID=$!
printf \\nPIP:START\\n >&8;rm /tmp/?pipe

これで、常にラインバッファ方式で出力を書き込むバックグラウンドスケジューラプロセスが確立されました。最初の呼び出しで、保存されたファイル記述子(親シェルの<>&9記述子に相当)に書き込むかコマンドを送信するかによってttyに書き込みます。

PIP:START
TTY:START

次の情報を送信できます。

printf \\nPIP:START\\n >&8
printf \\nTTY:START\\n >&8

あなたのニーズに応じて両方を処理します。親シェルはtrapコマンドを送信するように構成されています。TTY:停止それを受け取ったとき整数シグナル - だからそれを押すだけCtrl+Cキーボードの内容はいつでも端末への書き込みを中止するのに十分です。ただしtrap、必要に応じてパイプへの書き込みを停止するよう明示的に指示する必要があります。次のように停止するように言うことができます。

printf \\nPIP:STOP\\n >&8

sed〜するいつも w標準出力をバッファリングする方法に関係なく、ラインバッファに書き込みます。これにより、ping作成するすべての行を作成するとすぐに読み取ることができます。ファイル記述子9を読み取るために別のプロセスを呼び出すだけです。絵の中の絵 sed指示に従って作成したり、端末に直接印刷したりできます。読むには別のプロセスを呼び出してください。絵の中の絵ただし:

cat <&9

...または同様です。また、必要なすべてのプロセスがそのパイプにファイルハンドルを明示的に設定した後、使用中のパイプへのファイルシステムrmリンクを明示的に生成することに注意してください。mkfifoこれは、ファイルシステムを介して関連プロセスと対話できないことを意味します。すべてのIPCは、親シェルの記述子8と9を介して調整する必要があります。バックグラウンドプロセスsedpingプロセスのPIDはシェル変数$PNGPIDです$SEDPID。両方で解決可能ですkill

上記のスクリプト例を実行したので、次のように送信します。テレタイププライター:スタートコマンドの結果は次のとおりです。

[mikeserv@localhost ~]$ printf \\nTTY:START\\n >&8

[mikeserv@localhost ~]$ 218 24.2 ms
219 21.2 ms
220 23.1 ms
221 21.3 ms
222 21.9 ms
^C

[mikeserv@localhost ~]$

...しかし、両方のプロセスはまだ実行中です。sedどこにも出力は記録されません。

ps -Fp "$SEDPID" "$PNGPID"
UID        PID  PPID  C    SZ   RSS PSR STIME TTY      STAT   TIME CMD
mikeserv 31601 28945  0  2143  1712   4 14:22 pts/0    SN     0:00 sed -n s/^64.*q=\(.*\) ttl.*=/\1\
mikeserv 31602 28945  0  2106   740   3 14:22 pts/0    SN     0:00 ping -On 8.8.8.8

答え3

Unixはパイプに関しては本当に限られているようです。意味のある操作を実行する前に、まずファイルにデータを書き込んだようです。プロセス全体を再構築すると、仮想端末が解決策になる可能性があります。しかし、素晴らしいGUIを使用するのではなく、単に端末を使用し、awkを使用して進行状況バーをレンダリングしました。

ping -i 0.5  8.8.8.8 | gawk -F'[= ]' '/^64/{ for(i=0;i<($(NF-1)/3);i++) printf(" "); printf("|");  for(i=120;i>$(NF-1);i--) printf(" "); printf("\r");}'

120を端末の幅に変更します。

関連情報