Sepenuhnya buffer output perintah sebelum memipakan ke perintah lain?

10

Apakah ada cara untuk hanya menjalankan perintah setelah yang lain dilakukan tanpa file temp? Saya punya satu perintah lagi yang berjalan dan perintah lain yang memformat output dan mengirimkannya ke server HTTP menggunakan curl. Jika saya hanya menjalankan commandA | commandB, commandBakan mulai curl, terhubung ke server dan mulai mengirim data. Karena commandAbutuh waktu lama, server HTTP akan habis. Saya dapat melakukan apa yang saya inginkancommandA > /tmp/file && commandB </tmp/file && rm -f /tmp/file

Karena penasaran, saya ingin tahu apakah ada cara untuk melakukannya tanpa file temp. Saya mencoba mbuffer -m 20M -q -P 100tetapi proses keriting masih dimulai tepat di awal. Mbuffer menunggu sampai commandAselesai dengan benar-benar mengirim data. (Data itu sendiri hanya beberapa ratus kb maksimal)

Josef berkata Reinstate Monica
sumber
Bagaimana dengan commandA && commandB?
eyoung100
1
yang tidak mengirimkan output commandAke commandB, bukan?
Josef mengatakan Reinstate Monica
Ini memulai Perintah B jika Perintah A selesai dengan sukses, yang berarti bahwa ikal tidak dimulai lebih awal.
eyoung100
2
@ eyoung100 tetapi tidak lulus stdout dari commandA ke stdin untuk commandB, itulah yang dibutuhkan Josef!
roaima
Jika dia ingin keluaran berlalu, dia harus menggunakan file. Lihat jawaban cuonglm.
eyoung100

Jawaban:

14

Ini mirip dengan beberapa jawaban lainnya. Jika Anda memiliki paket "moreutils", Anda harus memiliki spongeperintah. Mencoba

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

The spongeperintah pada dasarnya adalah pass-melalui filter (seperti cat) kecuali bahwa ia tidak mulai menulis output sampai memiliki membaca seluruh masukan. Yaitu, itu "menyerap" data, dan kemudian melepaskannya ketika Anda memerasnya (seperti spons). Jadi, sampai batas tertentu, ini "curang" - jika ada jumlah data yang tidak sepele, spongehampir pasti menggunakan file sementara. Tapi itu tidak terlihat oleh Anda; Anda tidak perlu khawatir tentang hal-hal rumah tangga seperti memilih nama file yang unik dan membersihkannya setelah itu.

The { IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; } membaca baris pertama dari keluaran dari sponge. Ingat, ini tidak muncul sampai commandAselesai.  Kemudian menyala commandB, menulis baris pertama ke pipa, dan memanggil catuntuk membaca sisa output dan menulisnya ke pipa.

G-Man Mengatakan 'Reinstate Monica'
sumber
Terima kasih! Begitu spongejuga hal yang saya gunakan mbuffer, tetapi tampaknya lebih cocok di sini. menggunakan baca itu pintar di sini. Pasti akan mengingat ini untuk masa depan.
Josef berkata Reinstate Monica
@ Josef: Saya belum pernah mendengar mbuffersebelumnya; mungkin sebenarnya sama baiknya dengan sponge. Saya setuju bahwa menggunakan readadalah trik yang cerdas. Saya tidak bisa mengambil kredit penuh untuk itu; itu muncul dalam jawaban di seluruh Stack Exchange (U&L, Pengguna Super, Tanya Ubuntu, dll.) dari waktu ke waktu. Sebenarnya, jawaban roaima untuk pertanyaan ini sangat mirip dengan pertanyaan saya kecuali itu tidak menggunakan sponge(atau sesuatu yang setara), jadi, seperti yang saya sebutkan dalam komentar, itu tidak menunda memulai commandBsebanyak yang Anda butuhkan (dalam pemahaman saya tentang masalahmu).
G-Man Mengatakan 'Reinstate Monica'
github.com/ildar-shaimordanov/perl-utils#sponge memiliki versi skrip "spons" yang dibungkus dengan fungsi bash.
marinara
5

Perintah dalam saluran pipa dimulai secara bersamaan, Anda perlu menyimpan commandAoutput di suatu tempat untuk digunakan nanti. Anda dapat menghindari file temp dengan menggunakan variabel:

output=$(command A; echo A)
printf '%s' "${output%%A}" | commandB
cuonglm
sumber
Apa tujuan dari echo Aproses substitusi?
Digital Trauma
3
@DigitalTrauma: Ini mencegah substitusi perintah dari stripping trailing newlines.
cuonglm
Ah, saya mengerti, ya - tangkapan bagus dari sudut yang memungkinkan
Digital Trauma
Saya menyadari jawaban saya salah, jadi saya menghapusnya. Saya pikir ini terlihat benar sebagai gantinya. +1
Trauma Digital
Terima kasih banyak! Itu hebat dan terutama gema A / %% A untuk menjaga baris baru (bahkan jika saya tidak mengharuskannya)
Josef mengatakan Reinstate Monica
1

Saya tidak tahu adanya utilitas UNIX standar yang dapat mengatasi masalah ini. Satu opsi akan digunakan awkuntuk mengakumulasi commandAoutput dan menyiramnya commandBpada satu kesempatan, seperti itu

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

Berhati-hatilah karena ini bisa menjadi memori intensif karena awkmembangun string dari inputnya.

iruvar
sumber
1
Ini menghapus baris baru terakhir. Dengan asumsi bahwa karakter terakhir adalah baris baru, Anda memerlukan yang \nlain ORSdi bagian akhir.
G-Man Mengatakan 'Reinstate Monica'
0

Anda dapat menyelesaikan persyaratan dengan skrip kecil. Varian khusus ini menghindari file sementara dan potensi memori dengan mengorbankan proses tambahan.

#!/bin/bash
#
IFS= read LINE

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

    ) | "$@"
else
    exit 0
fi

Jika Anda memanggil skrip waituntil(dan membuatnya dapat dieksekusi, letakkan di PATH, dll), Anda akan menggunakannya seperti ini

commandA... | waituntil commandB...

Contoh

( sleep 3 ; date ; id ) | waituntil nl
roaima
sumber
1
Semua ini dilakukan adalah penundaan mulai commandBsampai commandAtelah menulis baris pertama output . Itu mungkin tidak cukup baik.
G-Man Mengatakan 'Reinstate Monica'
@ G-Man tapi kami tidak tahu. Saya suka Anda sponge. Tidak aktif untuk membaca yang itu sekarang.
roaima