プログラムが完了するのを待ってから完了したら、stdinをstdoutにパイプできますか?

プログラムが完了するのを待ってから完了したら、stdinをstdoutにパイプできますか?

コマンドが完了するのを待ってから、stdinをstdoutにパイプする方法を見つけようとしています。私はMacを使用していますが、私の質問は、Macに関連するものよりもプロセスが完了するのを待って出力をパイプする方法です。

Macでは、複数のコマンドを一緒に実行できます。演算子を使用してコマンドをリンクすると、次のsayコマンドを開始する前に各構文が完全に話されるまで待ちます。&&

$ say "stage 1" && say "stage 2"

これが実際のユースケースです。 bashスクリプトがあり、stdinをstdoutに渡したいと思います。後ろに終わりました。

$ cat /etc/passwd | say_and_pass "stage 1" | grep -v test | say_and_pass "stage 2"

したがって、概念的には、これは「フェーズ1」と大声で話し、すぐに「フェーズ2」と言い、次にgrepの内容を/etc/password標準出力にダンプします。

スクリプトの初期クラックはsay_and_pass次のとおりです。

話し合う

#!/usr/bin/env bash
OUT="$*"
say "$OUT" && cat 

しかし、うまくいかないようです;-)

編集:上記の例をsay_and_pass "stage2"最終コマンドとして使用するように変更しました。これは必要なものです。私のソリューション働く...

答え1

これが実際のユースケースです。何かを完了した後にstdinをstdoutに渡したいbashスクリプトがあります。

$ cat /etc/password | say_and_pass "stage 1" | grep -v test | say "stage 2"

ここでやろうとしていることの問題は、2つのデータストリームがあるように見えることです。 1つはテキストsay処理用で、もう1つはテキスト処理用です(例:catsum grep)。

データを混在させるため、単一のパイプラインは使用できません。 cat /etc/passwd | sayコンピュータはファイルの内容全体を読み取ろうとします。しかも書かsayないでください。何もない標準出力に変換するので、パイプには何も残りません。

データ処理ストリームを「中断」する他のユーティリティ(say一時ファイルなど)。

あなたのデモケースは例としてあまり役に立ちません。なぜならsaysにもかかわらずgrepファイルをpingするだけなのでgrep -v test /etc/passwdcat file | grep patternは次のようにすることができます。役に立たない使用cat)。

要約すると、次は一時ファイルを使用する例です。

scratch=$(mktemp)
trap "rm -f $scratch" EXIT
cat /etc/passwd > $scratch
say "Stage one"
grep -v test $scratch
say "Stage two"

そのうちの1つは、名前付きパイプを使用して次のことを行いますsay

mkfifo youtalktoomuch
say youtalktoomuch &
exec 3> youtalktoomuch
do_thing_one
echo "Stage one" > youtalktoomuch
do_thing_two
echo "Stage two" > youtalktoomuch
exec 3>&-
rm youtalktoomuch

Once again, though, if you need to connect the output of `do_thing_one` to the input of `do_thing_two` without piping one into the other, you will need to use either another named pipe or a scratch file on disk to hold the data.

答え2

このバージョンのsay_and_pass機能はOPの要件を満たしています。

#!/usr/bin/env bash

# Create a temporary file to store stdin
TEMP_STDIN="$(mktemp)"

# Capture stdin and save it to temporary file
cat - > "${TEMP_STDIN}"

# say message, but prevent any output to stdout
say "$@" > /dev/null

# Dump saved stdin to stdout
cat "${TEMP_STDIN}"

# Clean up, but prevent any output to stdout
rm -f "${TEMP_STDIN}" > /dev/null

grep@DopeGhotiが提案したものと同じ一時ファイルを使用しますが、スクリプトの外側に他のロジック(たとえば)を保持します。これにより、より多様で再利用が可能になりましたsay_and_pass

目標/目的の私の解釈は、潜在的に長いパイプラインの進行状況を監視するための聴覚的な方法が必要であることです。

したがって、実行するとき:

cmd1 | say_and_pass "stage 1" | cmd2 | say_and_pass "stage 2"

ユーザーは実行が完全に完了したことを音で知ることができます。ただし、完了するまで標準入力処理を開始できないことがcmd1欠点です。しかし、OPは妥協を認識し、それを喜んで実行したいようです。cmd2cmd1

これは@BradParksの答えと似ていますが、bashの利用可能なメモリ(@agcが指摘したように)によって制限されず、誤ってstdoutを汚染するのを防ぐのに少し注意が必要です。

また、メッセージを独自の変数にキャプチャする必要がないため、に渡すことも"$@"できますsay

答え3

私がしなければならないのは、スクリプトの先頭ですべてのstdinを消費することです... stdinが受信されたときにstdinを処理するのではなく、パイプラインチェーンの前のコマンド全体が完了したときに処理したいと思います。

ここに私のsay_and_passスクリプトがあります。私のユースケースに適しています!

#!/usr/bin/env bash

OUT=$(cat -)

MSG="$*"
say "$MSG"

echo "$OUT"

答え4

時間!

あなたの答えに応じてstdinをstdoutにダンプし、それを変数に保存する部分を削除することをお勧めします。これにより、メモリ消費量が減り、パイプラインチェーンがパイプラインと同じように動作します(入力された行が処理され、結果が次のプロセスに送信されます)。

#!/usr/bin/env bash

MSG="$*"
say "$MSG"

cat -

関連情報