Tubo semi-asincrono


11

Supponiamo che io abbia la seguente pipe:

a | b | c | d

Come posso attendere il completamento di c(o b) in sho bash? Ciò significa che lo script dpuò iniziare in qualsiasi momento (e non è necessario attendere) ma richiede un output completo cper funzionare correttamente.

Il caso d'uso è un difftoola gitche confronta le immagini. Viene chiamato da gite deve elaborare il suo input (la a | b | cparte) e visualizzare i risultati del confronto (la dparte). Il chiamante eliminerà l'input necessario per ae b. Ciò significa che prima di tornare dallo script, il processo c(o b) deve terminare. D'altra parte, non posso aspettare dperché questo significa che sto aspettando l'input dell'utente.

So di poter scrivere i risultati cin un file temporaneo, o forse usare un FIFO in bash. (Non sono sicuro che FIFO sarà di aiuto, però.) È possibile farlo senza file temporanei sh?

MODIFICARE

Forse sarebbe sufficiente se potessi scoprire l'ID del processo c(o b) in modo affidabile. Quindi è possibile avviare l'intera pipe in modo asincrono e attendere un ID processo. Qualcosa del genere

wait $(a | b | { c & [print PID of c] ; } | d)

EDIT 2 ^

Ho trovato una soluzione, i commenti (o soluzioni ancora migliori) sono i benvenuti.


Vuoi dire che vuoi diniziare l'elaborazione cdell'output solo dopo che cè stato completato? Non vuoi diniziare a elaborare ogni linea di output come viene?
terdon

@terdon: No, dè libero di iniziare quando vuole, ma cdeve finire prima di poter andare avanti.
krlmlr,

Sembra contraddittorio, se dpuò iniziare quando gli piace, cosa stai aspettando esattamente?
terdon

@terdon: espanso per mostrare il caso d'uso.
krlmlr,

Se dnon utilizza l'output di c, sembra non avere alcun senso fare dparte della pipeline. Ma se dusa l'input, allora ddeve lavorare un po 'sul suo input dopo averlo letto tutto affinché il tuo approccio faccia la differenza.
Hauke ​​Laging,

Risposte:


6
a | b | { c; [notify];} | d

La notifica può essere effettuata ad esempio tramite un segnale a un PID che è stato passato in una variabile di ambiente ( kill -USR1 $EXTPID) o creando un file ( touch /path/to/file).

Un'altra idea:

Esegui il processo successivo (quello per poter iniziare quello che stai aspettando) dalla pipeline:

a | b | { c; exec >&-; nextprocess;} | d

o

a | b | { c; exec >&-; nextprocess &} | d

Grazie. Ma poi, la creazione di un file temporaneo per l'output intermedio è più dettagliata e più facile da analizzare (per un essere umano). Inoltre, come posso evitare le condizioni di gara con il segnale? ... Speravo in una soluzione più pulita, ma se questa è la strada da percorrere, allora così sia.
krlmlr,

@krlmlr Quali condizioni di gara?
Hauke ​​Laging,

notifypotrebbe essere eseguito prima di impostare la trappola del segnale. Come posso assicurarmi di non perdere quel segnale?
krlmlr,

@krlmlr Si interrompe lo script che chiama la pipeline fino a quando non riceve un segnale stesso che viene inviato dopo che trapè stato definito.
Hauke ​​Laging,

La tua modifica: la pipe viene chiamata in loop, su cui non ho alcun controllo. Sfortunatamente, { c; bg; }o { c; exit 0; }non sembra funzionare.
krlmlr,

5

Se capisco correttamente la tua domanda, questo dovrebbe funzionare:

a | b | c | { (exec <&3 3<&-; d) &} 3<&0

(il trucco di fd 3 è perché alcune (la maggior parte) shell reindirizzano stdin a / dev / null con &).



3

Questo è ciò che ho trovato per tentativi ed errori, con l'aiuto dell'input di Hauke:

a | b | { c; kill -PIPE $$; } | d

Equivalentemente:

a | b | ( c; kill -PIPE $$; ) | d

(Quest'ultimo è più esplicito, perché {}verrà eseguito comunque in una subshell se all'interno di una pipe.)

Anche alcuni altri segnali (compresi QUIT, TERMe USR1) funzionano, tuttavia in questo caso la descrizione del segnale è mostrata sul terminale.

Mi chiedo se questa è l'intenzione originale del PIPEsegnale. Secondo il manuale :

SIGPIPE: Il PIPEsegnale viene inviato a un processo quando tenta di scrivere su una pipe senza un processo collegato all'altra estremità.

Ciò significa che quando invio artificialmente un segnale pipe alla subshell, termina silenziosamente, lasciando solo il consumatore finale ( d).

Questo funziona in entrambi she bash.


0

È possibile utilizzare il spongeprogramma dal pacchetto "moreutils":

a | b | c | sponge | d

spugna servirà per la fine cdell'output prima di collegarlo a d. Spero che sia quello che volevi.


0

Puoi semplicemente fare:

a | b | c | (d ; cat > /dev/null)

Quindi, al dtermine, catassorbirà il resto cdell'output fino al termine.

Ok. Dopo i commenti, penso che la risposta sia iniziare direttamente din background.

Fare:

a | b | c | (d &)

oppure usa la soluzione di Stephane Chazelas se ci sono problemi con la dlettura di stdin .


Temo che il mio caso d'uso sia piuttosto il contrario: cfinisce prima de devo aspettare fino alla cfine, ma non mi interessa d.
krlmlr,

Scusa, non capisco. Cosa vuoi fare quando cfinisce ed dè ancora in esecuzione? Uccidere d?
angus,

dha una GUI e può essere eseguita fino a quando l'utente non la chiude.
krlmlr,

OK ... ma questo già accade. Quando a, be cterminano il loro lavoro, chiudono stdout ed escono; e drimane solo in esecuzione. Vuoi inviare din background al ctermine? È così?
angus,

Sì, ddovrebbe essere inviato in background al ctermine.
krlmlr,
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.