combina l'output di due comandi in bash


81

È possibile combinare l'output di questi due comandi?

node ~/projects/trunk/index.js 
python ~/projects/trunk/run.py run

Nessuno dei due comandi esce quindi non sono sicuro di come farlo.


3
Se i programmi non finiscono, presumibilmente scrivono continuamente output? Cosa vuoi fare con il loro output? Linee interleave, ...? Perchè vuoi fare questo?
vonbrand,

2
Il comando node non genera molto, ma deve ancora essere eseguito. Il pitone emette tutte le richieste, voglio catturare entrambi e guardarli entrambi nella stessa finestra della shell.
amorevole

Risposte:


108

È possibile combinare due comandi raggruppandolo con { }:

{ command1 & command2; }

finora, puoi reindirizzare il gruppo su un file (l'ultimo ;prima }è obbligatorio):

{ command1 & command2; } > new_file

se vuoi separare STDOUTe STDERRin due file:

{ command1 & command2; } > STDOUT_file 2> STDERR_file

3
Non importa che i loro programmi non finiscano. 'tail -f' non "finisce" neanche, ma funziona ancora e combina i risultati di entrambi i programmi. Funziona anche per più di due comandi. ^ c per uscire uccide solo uno dei comandi raggruppati. Dovrai uccidere manualmente l'altro, però.
SuperMagic,

5
Sembra ti manca l'ultimo ;prima di }, è obbligatoria!
Gilles Quenot,

2
Attenzione: questo non preserva intere linee! Otterrai output inaffidabili man mano che le linee vengono divise parzialmente e confuse tra loro. Puoi provare questo con il { yes {1..20} & yes {1..20}; } | grep -v '^1 2 3'quale idealmente non stampa nulla se le linee non sono interrotte.
Antak,

8
Preferirei usare &&invece di &! command1 & command2- questo esegue command1 in background e avvia command2 immediatamente, quindi eseguendo entrambi i comandi in parallelo e rovinando l'output. command1 && command2- questo esegue command1 (in primo piano) e quindi, se command1 ha successo, esegue command2.
DUzun,

1
@DUzun OP ha detto che nessuno dei due comandi esce, quindi con la tua soluzione il secondo comando non verrà mai eseguito
Zoey Hewll

50

Più in generale, è possibile utilizzare una subshell o un raggruppamento di comandi e reindirizzare contemporaneamente l'output dell'intero gruppo.

Codice:

( command1 ; command2 ; command3 ) | cat

{ command1 ; command2 ; command3 ; } > outfile.txt

La differenza principale tra i due è che il primo si divide in un processo figlio, mentre il secondo opera nel contesto della shell principale. Ciò può avere conseguenze sull'impostazione e l'uso di variabili e altre impostazioni dell'ambiente, nonché sulle prestazioni.

Non dimenticare che la parentesi di chiusura nel raggruppamento di comandi (e funzioni) deve essere separata dal contenuto con un punto e virgola o una nuova riga. Questo perché in "}"realtà è un comando (parola chiave) a sé stante e deve essere trattato come tale.


2
Anche il reindirizzamento ( )funziona bene.
Muru,

2
}non è affatto un comando. È una parola riservata. Lo stesso vale per {. Io di solito scrivo queste liste in questo modo: { command1;command2;} > outfile.txt. È possibile aggiungere spazi dopo il punto e virgola ma non è necessario. Lo spazio dopo { è necessario, però.
Wildcard

1
Attenzione: questo non preserva intere linee! Otterrai output inaffidabili man mano che le linee vengono divise parzialmente e confuse tra loro. Puoi provare questo con il ( yes {1..20} & yes {1..20}; ) | grep -v '^1 2 3'quale idealmente non stampa nulla se le linee non sono interrotte. (Da H / t a @antak).
Ole Tange,

3
A volte vuoi eseguire command2 solo se ( command1 && command2 && command3 ) | cat
command1 ha avuto successo

preferisco le parentesi tonde ()come con le parentesi graffe {}che corre come un progresso in background e quindi devi gestire l'output da quello. Anche pipe to cat `| cat` è un'alternativa più piacevole di `> / dev / stdout`
DarkMukke

2

Ho finito per farlo, gli altri suggerimenti non hanno funzionato, dato che il secondo comando è stato ucciso o mai eseguito.

alias app () {
    nohup python ~/projects/trunk/run.py run 1>/tmp/log 2>&1 &
    echo $! > /tmp/api.pid
    nohup node ~/projects/trunk/index.js 1>/tmp/log 2>&1 &
    echo $! > /tmp/client.pid
    tail -f /tmp/log
}

1
Nota: ciò può causare errori di I / O se i due processi tentano di scrivere nel file "contemporaneamente".
Djizeus,

2
posso specificare 2 file di registro diversi e farlo tail -f *.logsebbene non abbia mai visto questo come un problema con 2 processi diversi che scrivono nello stesso file di registro.
amante

@chovy: potrebbe u scrivere il problema come questione qui ... è utile
Abdennour Toumi

1
Attenzione: questo non preserva intere linee! Otterrai output inaffidabili man mano che le linee vengono divise parzialmente e confuse tra loro. Puoi provarlo con yes {1..20}command1 = command2 = yes {1..20}e reindirizzare l'output combinato attraverso il | grep -v '^1 2 3'quale idealmente non stampa nulla se le linee non sono interrotte. (Da H / t a @antak).
Ole Tange,

Inoltre, il tuo disco potrebbe essere pieno se la quantità di dati è grande.
Ole Tange,

2

Prova questo:

paste $(node ~/projects/trunk/index.js) $(python ~/projects/trunk/run.py run) > outputfile

1
cosa fa 'incolla'?
amorevole

@chovy, vedi qui: techrepublic.com/article/… Non sono sicuro che funzionerà in questo contesto.
FixMaker

Non penso che incollare sia appropriato qui, poiché è pensato per mettere le colonne uno accanto all'altro
Bernhard

@Bernhard davvero. Ma non è stato specificato nel req's
frogstarr78,

@ frogstarr78 Penso che sia altamente improbabile che questo sia quello che vuole, ma hai ragione, non è specificato.
Bernhard,

1

La maggior parte delle soluzioni finora affronta male il problema della linea parziale. Supponiamo per un secondo che i programmi siano:

cmd1() {
    perl -e 'while(1) { print "a"x3000_000,"\n"}'
}
export -f cmd1
cmd2() {
    perl -e 'while(1) { print "b"x3000_000,"\n"}'
}
export -f cmd2

Quando si eseguono quelli in parallelo, si desidera che l'output abbia linee complete di as seguite da linee complete di bs. Quello che non vuoi è mescolare as e bs sulla stessa linea ( tr -s absostituisce la ripetizione di as con un singolo a, quindi è più facile vedere cosa succede):

# This is bad - half lines are mixed
$ (cmd1 & cmd2 ) | tr -s ab
bababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababa
ababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab

Se invece usi GNU Parallel, otterrai delle linee piene e pulite con as o bs ma mai mescolate:

$ parallel --line-buffer ::: cmd1 cmd2 | tr -s ab
a
a
b
b
b
b
a

Le versioni più recenti di GNU Parallel evitano addirittura di riempire il disco: quanto sopra può funzionare per sempre.


0

Dal momento che stai già utilizzando node, potresti voler provare contemporaneamente

Esegui più comandi contemporaneamente. Piace npm run watch-js & npm run watch-lessma meglio.


0

Per il caso speciale di combinare più output di comando BASH su una riga, ecco una ricetta per eseguire ogni comando a turno, rimuovendo eventuali nuove righe tra i loro output.

(echo 'ab' && echo 'cd' && echo 'ef') | tr -d '\n'
>>> abcdef

Come esempio del mondo reale, il codice seguente incorporerà un messaggio ASCII tra due stringhe fisse di byte (formando un comando di stampa, in questo caso)

#   hex prefix           encode a message as hex    hex suffix    | strip newline | hex to binary | (then, for example, send the binary over a TCP connection)
(echo '1b40' && echo "Test print #1" | xxd -p && echo '1d564103') | tr -d '\n'    | xxd -r -p     | nc -N 192.168.192.168 9100

(Nota: questo metodo funziona solo se i comandi escono. Per combinare stdout da comandi che non escono, vedere altre risposte.)


(1) Mostra l'output (previsto) del secondo comando. (2) Mostrare come l'OP userebbe questa tecnica per risolvere il suo problema.
Scott,

1) L'output del secondo comando è binario non ascii, quindi non sarebbe utile mostrarlo. 2) L'OP probabilmente ha risolto il suo problema specifico tra il 2013 e ora. Questa domanda ora è effettivamente un riferimento sulla combinazione dello stdout di più comandi Bash, quindi credo che una tecnica per combinarli su una riga sia una "ricetta" utile da menzionare qui (poiché sono venuto qui a cercarlo e non sono riuscito a trovare esso).
Luca,
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.