combine les sorties de deux commandes dans bash

81

Est-il possible de combiner les sorties de ces deux commandes?

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

Aucune des commandes ne se termine, donc je ne sais pas comment faire cela.

chovy
la source
3
Si les programmes ne finissent pas, ils écrivent probablement une sortie en continu? Que voulez-vous faire de leur sortie? Lignes d'entrelacement, ...? Pourquoi veux-tu faire cela?
vonbrand
2
La commande node ne génère pas beaucoup d'informations, mais elle doit encore être exécutée. Le python génère toutes les requêtes, je veux capturer les deux et les regarder toutes les deux dans la même fenêtre shell.
Chovy

Réponses:

108

Vous pouvez combiner deux commandes en la regroupant avec { }:

{ command1 & command2; }

jusqu'à présent, vous pouvez rediriger le groupe vers un fichier (le dernier ;avant }est obligatoire):

{ command1 & command2; } > new_file

si vous voulez séparer STDOUTet STDERRdans deux fichiers:

{ command1 & command2; } > STDOUT_file 2> STDERR_file
Gilles Quenot
la source
3
Peu importe que leurs programmes ne finissent pas. 'tail -f' ne "termine" pas non plus, mais cela fonctionne toujours et combine les sorties des deux programmes. Fonctionne aussi pour plus de deux commandes. ^ c pour quitter ne tue qu'une des commandes groupées. Vous devrez cependant tuer les autres manuellement.
SuperMagic
5
On dirait qu'il vous manque le dernier ;avant }, c'est obligatoire!
Gilles Quenot
2
Soyez averti: cela ne conserve pas les lignes entières! Vous obtiendrez des sorties peu fiables lorsque les lignes seront divisées en partie et mélangées les unes aux autres. Vous pouvez essayer ceci avec l' { yes {1..20} & yes {1..20}; } | grep -v '^1 2 3'idéal ne rien imprimer si les lignes ne sont pas brisées.
antak
8
Je préférerais utiliser &&au lieu de &! command1 & command2- ceci exécute commande1 en arrière-plan et démarre immédiatement commande2, exécutant ainsi les deux commandes en parallèle et perturbant la sortie. command1 && command2- ceci exécute commande1 (au premier plan), puis, si commande1 est intégrée, commande2.
DUzun
1
@DUzun OP a indiqué qu'aucune commande n'existait, donc avec votre solution, la deuxième commande ne sera jamais exécutée
Zoey Hewll
50

Plus généralement, il est possible d'utiliser un sous-shell ou un groupe de commandes et de rediriger la sortie de l'ensemble du groupe en même temps.

Code:

( command1 ; command2 ; command3 ) | cat

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

La principale différence entre les deux réside dans le fait que le premier divise un processus enfant, tandis que le second opère dans le contexte du shell principal. Cela peut avoir des conséquences sur la définition et l'utilisation de variables et d'autres paramètres d'environnement, ainsi que sur les performances.

N'oubliez pas que le crochet de fermeture du groupe de commandes (et des fonctions) doit être séparé du contenu par un point-virgule ou un retour à la ligne. En effet, il "}"s’agit en réalité d’une commande (mot-clé) qui doit être traitée comme telle.

j9s
la source
2
La redirection à partir de ( )fonctionne bien aussi.
Muru
2
}n'est pas une commande du tout. C'est un mot réservé. Même chose pour {. Je l' habitude d' écrire ces listes comme ceci: { command1;command2;} > outfile.txt. Vous pouvez ajouter des espaces après les points-virgules, mais ce n'est pas nécessaire. L'espace après { est nécessaire, cependant.
Wildcard
1
Soyez averti: cela ne conserve pas les lignes entières! Vous obtiendrez des sorties peu fiables lorsque les lignes seront divisées en partie et mélangées les unes aux autres. Vous pouvez essayer ceci avec l' ( yes {1..20} & yes {1..20}; ) | grep -v '^1 2 3'idéal ne rien imprimer si les lignes ne sont pas brisées. (H / t à @antak).
Ole Tange
3
Parfois, vous ne souhaitez exécuter commande2 que si commande1 a réussi:( command1 && command2 && command3 ) | cat
DUzun
Je préfère les crochets ronds ()car avec les accolades, {}cela fonctionne comme un progrès en arrière-plan et ensuite vous devez traiter le résultat. Aussi pipe au chat `| cat` est une alternative plus intéressante que `> / dev / stdout`
DarkMukke le
2

J'ai fini par le faire, les autres suggestions ne fonctionnaient pas, car la 2e commande avait été tuée ou n'avait jamais été exécutée.

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
}
chovy
la source
1
Remarque: cela peut entraîner des erreurs d'E / S si les deux processus tentent d'écrire dans le fichier "en même temps".
Djizée
2
pouvez spécifier 2 fichiers journaux différents et le faire tail -f *.logbien que je n’aie jamais vu cela comme un problème avec 2 processus différents écrivant dans le même fichier journal.
Chovy
@chovy: pourriez-vous écrire votre problème sous forme de question ici ... il est utile
Abdennour TOUMI
1
Soyez averti: cela ne conserve pas les lignes entières! Vous obtiendrez des sorties peu fiables lorsque les lignes seront divisées en partie et mélangées les unes aux autres. Vous pouvez essayer ceci avec command1 = yes {1..20}command2 = yes {1..20}et diriger la sortie combinée à travers | grep -v '^1 2 3'laquelle, idéalement, ne rien imprimer si les lignes ne sont pas rompues. (H / t à @antak).
Ole Tange
De plus, votre disque peut être saturé si la quantité de données est importante.
Ole Tange
2

Essaye ça:

paste $(node ~/projects/trunk/index.js) $(python ~/projects/trunk/run.py run) > outputfile
frogstarr78
la source
1
que fait 'coller'?
Chovy
@chovy, voir ici: techrepublic.com/article/… Je ne sais pas si cela fonctionnera dans ce contexte.
FixMaker
Je ne pense pas que la pâte soit appropriée ici, car elle est censée mettre des colonnes l'une à côté de l'autre
Bernhard
@ Bernhard en effet. Mais ce n'était pas spécifié dans les req's
frogstarr78
@ frogstarr78 Je pense qu'il est hautement improbable que ce soit ce qu'il souhaite, mais vous avez raison, cela n'est pas précisé.
Bernhard
1

À ce jour, la plupart des solutions s’attaquent mal au problème de la ligne partielle. Supposons pendant une seconde que les programmes sont:

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

Lorsque vous exécutez ceux-ci en parallèle, vous voulez que la sortie ait des lignes complètes de as suivies de lignes complètes de bs. Ce que vous ne voulez pas, c'est mélanger as et bs sur la même ligne ( tr -s abremplace la répétition apar un seul a, il est donc plus facile de voir ce qui se passe):

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

Si vous utilisez plutôt GNU Parallel, vous obtenez de belles lignes complètes avec as ou bs mais jamais mélangées:

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

Les versions les plus récentes de GNU Parallel évitent même de remplir votre disque: les opérations ci-dessus peuvent durer indéfiniment.

Ole Tange
la source
0

Puisque vous utilisez déjà node, vous voudrez peut-être essayer simultanément

Exécutez plusieurs commandes simultanément. J'aime npm run watch-js & npm run watch-lessmais mieux.

Tamlyn
la source
0

Pour le cas particulier de la combinaison de plusieurs sorties de commande BASH sur une seule ligne, voici une recette pour exécuter chaque commande à son tour, en supprimant les nouvelles lignes entre leurs sorties.

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

Par exemple, le code ci-dessous incorporera un message ASCII entre deux chaînes d'octets fixes (formant une commande d'impression, dans ce cas).

#   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

(Remarque: cette méthode ne fonctionne que si les commandes sont fermées. Pour combiner stdout à partir de commandes non fermées, voir les autres réponses.)

Luke
la source
(1) S'il vous plaît montrer la sortie (attendue) de votre deuxième commande. (2) Veuillez montrer comment le PO utiliserait cette technique pour résoudre son problème.
Scott
1) La sortie de la deuxième commande est non-binaire ascii, il ne serait donc pas utile de l'afficher. 2) OP a probablement résolu son problème spécifique entre 2013 et maintenant. Cette question est maintenant effectivement une référence sur la combinaison de la sortie standard de plusieurs commandes Bash. Je pense donc qu'une technique permettant de les combiner sur une seule ligne est une "recette" utile à mentionner ici (étant donné que je suis venu ici pour la rechercher et que je il).
Luke