Buffer di output del comando completamente prima di eseguire il piping a un altro comando?


10

C'è un modo per eseguire un comando solo dopo che un altro è stato fatto senza un file temporaneo? Ho un comando in esecuzione più lungo e un altro comando che formatta l'output e lo invia a un server HTTP usando curl. Se eseguo solo commandA | commandB, commandBinizierò curl, mi collegherò al server e inizierò a inviare dati. Perché commandAimpiega così tanto tempo, il server HTTP scadrà. Posso fare quello che vogliocommandA > /tmp/file && commandB </tmp/file && rm -f /tmp/file

Per curiosità, voglio sapere se esiste un modo per farlo senza il file temporaneo. Ho provato mbuffer -m 20M -q -P 100ma il processo di arricciatura è ancora iniziato all'inizio. Mbuffer attende solo fino al commandAtermine dell'invio effettivo dei dati. (I dati stessi sono solo poche centinaia di kb al massimo)


Che dire commandA && commandB?
eyoung100

1
che non trasmette l'output di commandAa commandB, vero?
Josef dice di reintegrare Monica il

Avvia il comando B se il comando A viene completato correttamente, il che significa che l'arricciatura non inizia in anticipo.
eyoung100

2
@ eyoung100 ma non passa stdout da commandA a stdin per commandB, che è ciò di cui Josef ha bisogno!
roaima,

Se vuole che l'output sia passato, deve usare un file. Vedi la risposta di cuonglm.
eyoung100

Risposte:


14

Questo è simile a un paio di altre risposte. Se hai il pacchetto "moreutils", dovresti avere il spongecomando. Provare

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

Il spongecomando è sostanzialmente un filtro pass-through (come cat) tranne per il fatto che non inizia a scrivere l'output fino a quando non ha letto l'intero input. Vale a dire, "assorbe" i dati e quindi li rilascia quando li stringi (come una spugna). Quindi, in una certa misura, si tratta di un "imbroglio" - se c'è una quantità non banale di dati, spongequasi sicuramente utilizza un file temporaneo. Ma è invisibile per te; non devi preoccuparti di cose di pulizia come la scelta di un nome file univoco e la pulizia successiva.

La { IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; } legge la prima linea di uscita da sponge. Ricorda, questo non appare fino al commandAtermine.  Quindi si accende commandB, scrive la prima riga nella pipe e invoca catper leggere il resto dell'output e scriverlo nella pipe.


Grazie! Così spongefa quella cosa che ho usato mbufferper, ma sembra essere più adatto qui. usare leggi è intelligente qui. Lo ricorderò sicuramente per il futuro.
Josef dice di reintegrare Monica il

@Josef: non ne ho mai sentito parlare mbufferprima; potrebbe effettivamente essere altrettanto buono sponge. Sono d'accordo che l'utilizzo readè un trucco intelligente. Non posso prendermi il pieno merito per questo; compare di volta in volta in risposte tramite Stack Exchange (U&L, Super User, Ask Ubuntu, ecc.). In effetti, la risposta di roaima a questa domanda è molto simile alla mia, tranne per il fatto che non usa sponge(o qualcosa di equivalente), quindi, come ho detto in un commento, non tarda a iniziare commandBquanto serve (nella mia comprensione di il tuo problema).
G-Man dice "Ripristina Monica" il

github.com/ildar-shaimordanov/perl-utils#sponge ha una versione script di "sponge" avvolta in una funzione bash.
Marinara

5

I comandi nella linea di tubazioni vengono avviati contemporaneamente, è necessario memorizzare l' commandAoutput da qualche parte per utilizzarlo in seguito. È possibile evitare il file temporaneo usando la variabile:

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

Qual è lo scopo della echo Asostituzione nel processo?
Trauma digitale

3
@DigitalTrauma: Impedisce la sostituzione del comando dallo stripping di nuove righe.
cuonglm

Ah, capisco, sì - ottima cattura di un possibile caso d'angolo
Digital Trauma

Mi sono reso conto che la mia risposta era errata, quindi l'ho cancellata. Penso invece che sia corretto. +1
Trauma digitale

Molte grazie! Questo è fantastico e soprattutto l'eco A / %% A per mantenere la nuova riga (anche se non lo richiedo)
Josef dice Reinstate Monica

1

Non conosco alcuna utility UNIX standard in grado di risolvere questo problema. Un'opzione sarebbe quella awkdi accumulare commandAoutput e scaricarlo commandBin un colpo solo, in questo modo

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

Attenzione che questo potrebbe richiedere molta memoria poiché awksta costruendo una stringa dal suo input.


1
Questo elimina l'ultima riga. Supponendo che l'ultimo personaggio sia una newline, alla fine hai bisogno di uno \no l'altro ORS.
G-Man dice "Ripristina Monica" il

0

È possibile risolvere il requisito con un piccolo script. Questa particolare variante evita il file temporaneo e il potenziale hog della memoria a spese di processi aggiuntivi.

#!/bin/bash
#
IFS= read LINE

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

    ) | "$@"
else
    exit 0
fi

Se dovessi chiamare lo script waituntil(e renderlo eseguibile, inserirlo in PATH, ecc.), Lo useresti in questo modo

commandA... | waituntil commandB...

Esempio

( sleep 3 ; date ; id ) | waituntil nl

1
Tutto ciò fa ritardare l'avvio commandBfino a quando non commandAha scritto la sua prima riga di output . Questo probabilmente non è abbastanza buono.
G-Man dice "Ripristina Monica" il

@ G-Man ma non lo sappiamo. Mi piace il tuo sponge. Off per leggere su quello ora.
roaima,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.