combinar saída de dois comandos no bash

81

É possível combinar a saída desses dois comandos?

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

Nenhum comando sai, então não tenho certeza de como fazer isso.

chovy
fonte
3
Se os programas não terminarem, presumivelmente eles gravam a saída continuamente? O que você quer fazer com a saída deles? Entrelaçar linhas, ...? Por que você quer fazer isso?
vonbrand
2
O comando node não gera muito, mas ainda precisa ser executado. O python gera todos os pedidos, quero capturar os dois e assisti-los na mesma janela do shell.
chovy

Respostas:

108

Você pode combinar dois comandos agrupando-o com { }:

{ command1 & command2; }

até o momento, você pode redirecionar o grupo para um arquivo (a última ;antes }é obrigatória):

{ command1 & command2; } > new_file

se você deseja separar STDOUTe STDERRem dois arquivos:

{ command1 & command2; } > STDOUT_file 2> STDERR_file
Gilles Quenot
fonte
3
Não importa que os programas não terminem. 'tail -f' também não "termina", mas ainda funciona e combina as saídas de ambos os programas. Funciona para mais de dois comandos também. ^ c para sair mata apenas um dos comandos agrupados. Você terá que matar os outros manualmente, no entanto.
SuperMagic
5
Parece que você não tem o último ;antes }, é obrigatório!
Gilles Quenot
2
Esteja avisado: isso não preserva linhas inteiras! Você obterá resultados não confiáveis ​​à medida que as linhas se separam parcialmente e se misturam. Você pode tentar isso com o { yes {1..20} & yes {1..20}; } | grep -v '^1 2 3'qual, idealmente, não imprimirá nada se as linhas não estiverem quebradas.
antak
8
Eu prefiro usar em &&vez de &! command1 & command2- isso executa o comando1 em segundo plano e inicia o comando2 imediatamente, executando ambos os comandos em paralelo e atrapalhando a saída. command1 && command2- isso executa o comando1 (em primeiro plano) e, em seguida, se o comando1 for bem-sucedido, executa o comando2.
DUzun 5/04
1
O @DUzun OP disse que nenhum dos comandos sai; portanto, com a sua solução, o segundo comando nunca será executado
Zoey Hewll
50

De maneira mais geral, é possível usar um subshell ou um agrupamento de comandos e redirecionar a saída de todo o grupo de uma só vez.

Código:

( command1 ; command2 ; command3 ) | cat

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

A principal diferença entre os dois é que o primeiro divide um processo filho, enquanto o segundo opera no contexto do shell principal. Isso pode ter consequências em relação à configuração e uso de variáveis ​​e outras configurações do ambiente, bem como ao desempenho.

Não esqueça que o colchete de fechamento no agrupamento de comandos (e funções) deve ser separado do conteúdo por ponto e vírgula ou por uma nova linha. Isso ocorre porque, "}"na verdade, é um comando (palavra-chave) próprio e deve ser tratado como um.

j9s
fonte
2
Redirecionamento de ( )funciona bem também.
Muru
2
}não é um comando. É uma palavra reservada. O mesmo vale para {. Eu costumo escrever tais listas assim: { command1;command2;} > outfile.txt. Você pode adicionar espaços após o ponto e vírgula, mas não é necessário. O espaço depois { é necessário, no entanto.
Curinga
1
Esteja avisado: isso não preserva linhas inteiras! Você obterá resultados não confiáveis ​​à medida que as linhas se separam parcialmente e se misturam. Você pode tentar isso com o ( yes {1..20} & yes {1..20}; ) | grep -v '^1 2 3'qual, idealmente, não imprimirá nada se as linhas não estiverem quebradas. (H / t para @antak).
precisa
3
Às vezes você deseja executar command2 somente se command1 sucedido:( command1 && command2 && command3 ) | cat
DUzun
Eu prefiro os parênteses redondos, ()como os parênteses, {}que são executados como um progresso em segundo plano e, em seguida, você deve lidar com a saída disso. Também tubo para gato `| cat` é uma alternativa mais agradável do que `> / dev / stdout` #
DarkMukke
2

Acabei fazendo isso, as outras sugestões não funcionaram, pois o 2º comando foi morto ou nunca executado.

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
fonte
1
Nota: isso pode causar erros de E / S se os dois processos tentarem gravar no arquivo "ao mesmo tempo".
Djizeus
2
pode especificar 2 arquivos de log diferentes e, tail -f *.logembora nunca tenha visto isso como um problema, com 2 processos diferentes gravando no mesmo arquivo de log.
Chovy
@chovy: u poderia escrever o seu problema como questão aqui ... é útil
Abdennour Toumi
1
Esteja avisado: isso não preserva linhas inteiras! Você obterá resultados não confiáveis ​​à medida que as linhas se separam parcialmente e se misturam. Você pode tentar isso com yes {1..20}command1 = command2 = yes {1..20}e canalizar a saída combinada através da | grep -v '^1 2 3'qual idealmente não imprimirá nada se as linhas não estiverem quebradas. (H / t para @antak).
precisa
Além disso, seu disco pode ficar cheio se a quantidade de dados for grande.
precisa
2

Tente o seguinte:

paste $(node ~/projects/trunk/index.js) $(python ~/projects/trunk/run.py run) > outputfile
frogstarr78
fonte
1
o que 'colar' faz?
chovy
@ Chovy, veja aqui: techrepublic.com/article/… Não tenho certeza se funcionará nesse contexto.
FixMaker 14/02
Eu não acho que colar é apropriado aqui, uma vez que se destina a colocar colunas lado uns dos outros
Bernhard
@ Bernhard de fato. Mas não foi especificado no req's
frogstarr78 16/02
@ frogstarr78 Eu acho que é altamente improvável que isso é o que ele quer, mas você está certo, não está especificado.
Bernhard
1

Até agora, a maioria das soluções lida mal com o problema de linha parcial. Suponha por um segundo que os programas sejam:

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

Ao executar aqueles em paralelo, você deseja que a saída tenha linhas completas de as seguidas por linhas completas de bs. O que você não quer é as e bs de mistura na mesma linha ( tr -s absubstitui repetindo as com um único a, por isso é mais fácil de ver o que acontece):

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

Se você usar o GNU Parallel, obterá linhas completas limpas com as ou bs, mas nunca misturadas:

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

As versões mais recentes do GNU Parallel evitam o preenchimento do seu disco: O acima pode ser executado para sempre.

Ole Tange
fonte
0

Como você já está usando node, convém tentar simultaneamente

Execute vários comandos simultaneamente. Como npm run watch-js & npm run watch-lessmas melhor.

Tamlyn
fonte
0

Para o caso especial de combinar várias saídas de comando BASH em uma linha, aqui está uma receita para executar cada comando sucessivamente, removendo novas linhas entre suas saídas.

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

Como um exemplo do mundo real, o código abaixo incorporará uma mensagem ASCII entre duas cadeias de bytes fixas (formando um comando de impressão, neste 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: este método funciona apenas se os comandos forem encerrados. Para combinar stdout de comandos que não saem, consulte outras respostas.)

Lucas
fonte
(1) Por favor, mostre a saída (esperada) do seu segundo comando. (2) Por favor, mostre como o OP usaria essa técnica para resolver seu problema.
Scott
1) A saída do segundo comando é binária não-ascii, portanto, não seria útil mostrá-lo. 2) O OP provavelmente resolveu seu problema específico entre 2013 e agora. Esta questão agora é efetivamente uma referência na combinação do stdout de vários comandos do Bash, por isso acredito que uma técnica para combiná-los em uma linha é uma "receita" útil a ser mencionada aqui (desde que eu vim aqui procurando e não consegui encontrar isto).
Luke