web-dev-qa-db-ja.com

別のコマンドにパイプする前にコマンド出力を完全にバッファリングしますか?

一時ファイルなしでコマンドが実行された後にのみコマンドを実行する方法はありますか?もう1つの実行中のコマンドと、出力をフォーマットしてcurlを使用してHTTPサーバーに送信する別のコマンドがあります。 commandA | commandBcommandBcurlを開始し、サーバーに接続してデータの送信を開始します。 commandAには非常に時間がかかるため、HTTPサーバーはタイムアウトします。 commandA > /tmp/file && commandB </tmp/file && rm -f /tmp/file

好奇心から、一時ファイルなしでそれを行う方法があるかどうか知りたいです。私は試した mbuffer -m 20M -q -P 100しかし、カールプロセスはまだ最初から開始されます。 Mbufferは、実際にデータを送信してcommandAが完了するまで待機します。 (データ自体は最大で数百kbです)

これは他のいくつかの回答と似ています。 「moreutils」パッケージがある場合は、spongeコマンドが必要です。試す

commandA | sponge | { IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; }

spongeコマンドは基本的にパススルーフィルターです(catと同様)。ただし、入力全体を読み取るまで出力の書き込みを開始しません。つまり、データを「吸収」し、(スポンジのように)握るとデータを解放します。したがって、ある程度、これは「不正行為」です。重要な量のデータがある場合、spongeはほぼ確実に一時ファイルを使用します。しかし、それはあなたには見えません。一意のファイル名の選択や後のクリーンアップなど、ハウスキーピングについて心配する必要はありません。

{ IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; }は、出力の最初の行をspongeから読み取ります。これは、commandAが完了するまで表示されないことに注意してください。 ThencommandBを起動し、最初の行をパイプに書き込み、catを呼び出して残りの出力を読み取り、パイプに書き込みます。

パイプラインのコマンドは同時に開始されます。後で使用するには、commandAの出力をどこかに保存する必要があります。変数を使用して一時ファイルを回避できます。

output=$(command A; echo A)
printf '%s' "${output%%A}" | commandB
4
cuonglm

この問題に対処できる標準のUNIXユーティリティは知りません。 1つのオプションは、awkを使用してcommandA出力を累積し、commandBに一度にフラッシュすることです。

commandA  | awk '{x = x ORS $0}; END{printf "%s", x | "commandB"}'

awkは入力から文字列を構築しているため、これはメモリを大量に消費する可能性があることに注意してください。

1
iruvar

小さなスクリプトで要件を解決できます。この特定の亜種は、追加のプロセスを犠牲にして、一時ファイルと潜在的なメモリ占有を回避します。

#!/bin/bash
#
IFS= read LINE

if test -n "$LINE"
then
    test -t 2 && echo "Starting $*" >&2
    (
        echo "$LINE"
        cat

    ) | "$@"
else
    exit 0
fi

スクリプトwaituntilを呼び出して実行可能にしたり、PATHに入れたりする場合は、次のように使用します

commandA... | waituntil commandB...

( sleep 3 ; date ; id ) | waituntil nl
0
roaima