Combine vários comandos unix em uma saída

9

Preciso pesquisar em nossos logs de email um endereço de email específico. Mantemos um arquivo atual chamado maillog , além de uma semana de arquivos .bz2 na mesma pasta. Atualmente, estou executando os seguintes comandos para procurar o arquivo:

grep person@domain.com maillog
bzgrep person@domain.com *.bz2

Existe uma maneira de combinar os comandos grepe bzgrepem uma única saída? Dessa forma, eu poderia canalizar os resultados combinados para um único email ou um único arquivo.

Ben McCormack
fonte

Respostas:

24

Outra maneira é

{ grep ...; bzgrep ...;} >file

&&tem a dificuldade de que o bzgrepnão seria executado se o grepfalhasse.

Observe o espaço obrigatório após a chave de abertura e o ponto e vírgula após o último comando. Como alternativa, você pode usar a sintaxe do subshell (parênteses em vez de chaves), que não é tão exigente:

(grep ...; bzgrep ...) >file
geekosaur
fonte
+1 para obter a melhor abordagem de shell de uso geral para esse problema. O agrupamento de shell é um recurso geralmente subutilizado.
Phil Hollenback
2
Você não quer dizer em ()vez de {}?
Ehtesh Choudhury
No arch linux, isso funcionou com, ()mas não {}, de qualquer maneira +1, pois é disso que eu precisava!
31715 Chris Magnuson
11

O bzgrep assume automaticamente o grep normal se um arquivo não estiver compactado com bzip. Portanto, o seguinte deve ser suficiente:

bzgrep person@domain.com maillog *bz2 | mail -s "logs yay" someuser@blah

Ah, claro, aqui está minha solução obrigatória GNU Parallel também:

parallel -m bzgrep person@domain.com ::: maillog* *bz2 | mail -s "logs yay" someuser@blah

o que pode ser muito mais rápido se você estiver verificando muitos arquivos.

Phil Hollenback
fonte
2

Aqui está outra maneira de fazer isso (supondo que você esteja executando o bash, o que provavelmente é):

cat <(bzgrep ...) <(grep ...)

Aqui, o bash alimenta de maneira transparente a saída dos comandos bzgrep e grep no gato como se fossem arquivos (e meio que estão sob o capô, detalhes em url na parte inferior).

No seu caso particular, eu recomendaria a solução de Phil, mas o acima é um bom truque para manter em sua bolsa.

Se você estiver interessado, pode ler mais aqui: http://www.tldp.org/LDP/abs/html/process-sub.html

Drew Bloechl
fonte
Ah, sim, a resposta canônica para 'como você difere a saída de dois processos'. Um bom truque para saber.
Phil Hollenback
1

No momento em que escrevi isso, a sintaxe da resposta aceita estava errada para a maioria, senão todas, as conchas derivadas de Bourne, inclusive bash. Sugeri uma edição na parte superior e aceitei a resposta para corrigir isso, mas também estava inclinado a adicionar todas essas outras informações, e isso teria sido mais uma reescrita do que uma edição.

Você pode usar comandos compostos:

{ grep ...; bzgrep ...; } >file

..ou subcascas (observe os parênteses em vez de chaves):

(grep ...; bzgrep ...) >file

..para agrupar os comandos. A maneira do subshell possui uma sintaxe melhor (mais tolerante à falta de espaço em branco e permite que você omita o último ponto e vírgula), mas cria um novo processo ou "finge" executando os comandos em um ambiente limpo. Ambos têm vantagens dependendo do que você deseja fazer, o que não importa aqui, mas vale a pena procurar se você quiser mais proficiência com o shell.

Nota: Você também pode usar o pipelining com esses truques, para fazer algo assim:

{ grep ...; bzgrep ...; } | less

PS se você não se preocupam com a ordem das partidas em sua produção combinada, você pode usar um único &entre os dois comandos, assim: { grep ... & bzgrep ...; }. Em seguida, os dois comandos são executados simultaneamente: o grepé lançado e o shell o coloca em segundo plano; então, o shell executa o bzgrep. (Mas há uma pequena ressalva sobre isso, com a explicação que envolve o redirecionamento de arquivos e o buffer de fluxo de arquivos, potencialmente fazendo com que uma parte muito pequena das linhas no arquivo de saída seja dividida / mutilada: se você verá isso, isso dependerá de como grep, bzgrep, e as libc stdio.hfunções são implementadas. Na maioria das implementações, acredito que canalizar o comando antes de redirecionar para um arquivo evitará o problema; portanto, você pode fazer isso { foo & bar; } | cat - >filecomo uma solução alternativa.)

mtraceur
fonte
0

Você pode amarrar os comandos com &&, o que permitirá executar cada comando.

você também pode adicionar >> textfile.txt ao final de cada comando e fazer com que a saída atinja um arquivo e envie-o por correio.

Mike
fonte
2
Como o geekosaur mencionado, && não deve ser usado aqui porque o valor de retorno do grep depende se ele encontrou algo ou não. Se você o fizesse grep ... && bzgrep ..., se o grep não tivesse acertos, ele retornaria a falha e o comando pararia. >>é uma boa idéia, porém, ao contrário >, ele adiciona saída ao final de um arquivo existente.
DerfK 8/03/11
certo, eu tinha esquecido a condição de saída, impedindo o segundo comando.
Mike