Como mesclar e canalizar resultados de dois comandos diferentes para um único comando?

35

Quero mesclar (união) a saída de dois comandos diferentes e canalizá-los para um único comando.

Um exemplo bobo:

Comandos que eu quero mesclar a saída:

cat wordlist.txt
ls ~/folder/*

para dentro:

wc -l

Neste exemplo, se o wordlist.txt contiver 5 linhas e 3 arquivos, desejo wc -lretornar 8.

$cat wordlist.txt *[magical union thing]* ls ~/folder/* | wc -l
8

Como faço isso?

David Oneill
fonte

Respostas:

56

Sua união mágica é um ponto-e-vírgula ... e chaves:

    { cat wordlist.txt ; ls ~/folder/* ; } | wc -l

Os chavetas estão agrupando apenas os comandos, de modo que o sinal de |canal afeta a saída combinada.

Você também pode usar parênteses em ()torno de um grupo de comandos, que executaria os comandos em uma subshell. Isso tem um conjunto sutil de diferenças com chaves, por exemplo, tente o seguinte:

    cd $HOME/Desktop ; (cd $HOME ; pwd) ; pwd
    cd $HOME/Desktop ; { cd $HOME ; pwd ; } ; pwd

Você verá que todas as variáveis ​​de ambiente, incluindo o diretório de trabalho atual, são redefinidas após a saída do grupo de parênteses, mas não após a saída do grupo de chaves.

Quanto ao ponto e vírgula, as alternativas incluem os sinais &&e ||, que executam condicionalmente o segundo comando somente se o primeiro for bem-sucedido ou não, respectivamente, por exemplo,

    cd $HOME/project && make
    ls $HOME/project || echo "Directory not found."
pablomme
fonte
1
Isso funciona! Ajude-me a aprender - o que exatamente as chaves estão fazendo aqui? Que outros poderes mágicos eles têm?
David Oneill
@DavidOneill { list; }é um comando composto . From bash(1): list é simplesmente executado no ambiente atual do shell. A lista deve ser finalizada com uma nova linha ou ponto e vírgula. [..] O status de retorno é o status de saída da lista.
Lekensteyn
Adicionei um pouco mais de informação à resposta. O guia definitivo para scripts de shell com o bash é a página de manual do bash, digite man bashna linha de comando e procure-o.
Pablomme
deve o; nesses comandos não será && caso o arquivo wordlist.txt não exista?
Rinzwind
Se wordlist.txtnão existir, um erro de cataparecerá no erro padrão, mas não na saída padrão, portanto wc -l, não contará nenhuma linha a partir dele. O mesmo vale para ~/foldernão existir. Pode-se adicionar 2> /dev/nullentre cada um desses comandos e seu ponto e vírgula para evitar ruído no erro padrão, mas, além de feias, as mensagens de erro são inofensivas com a finalidade de contar as linhas.
Pablomme
8

Como wcaceita um caminho de arquivo como entrada, você também pode usar a substituição de processo:

wc -l <(cat wordlist.txt; ls ~/folder/*)

Isso equivale aproximadamente a:

echo wordlist.txt > temp
ls ~/folder/* >> temp
wc -l temp

Mente que ls ~/folder/*também retorna o conteúdo dos subdiretórios, se houver (devido à expansão glob). Se você quiser apenas listar o conteúdo ~/folder, use ls ~/folder.

Lekensteyn
fonte
O wc -lfoi apenas um exemplo inventado, na verdade estou colocando em algo mais complicado que não tem essa opção.
David Oneill
2
@DavidOneill Bem, como cataceita um argumento de arquivo, você pode usar cat <(cat wordlist.txt; ls wordlist.txt) | wc -le até cat wordlist.txt <(ls wordlist.txt) | wc -l. É claro que isso é muito feio, mas demonstra as infinitas possibilidades com as ferramentas de linha de comando.
Lekensteyn
1
@DavidOneill Correto, eu o corrigi agora, obrigado.
Lekensteyn
1
Você deseja ls ~/folder/que, se ~/folderfor um link simbólico, lsliste o conteúdo de seu destino em vez do próprio link. A propósito, todos os lscomandos devem ser seguidos -1para que um único arquivo por linha seja impresso e wc -lseja uma maneira válida de contar arquivos.
Pablomme
@ pablomme Você é verdadeiro sobre a coisa do link simbólico, mas -1está implícito, pois a saída não é um terminal, mas um tubo.
Lekensteyn
2

Eu estava me perguntando a mesma pergunta e acabei escrevendo um roteiro curto.

magicalUnionThing(Eu chamo append):

#!/bin/sh
cat /dev/stdin
$*

Tornar esse script executável

chmod +x ./magicalUnionThing

Agora você faz

cat wordlist.txt |./magicalUnionThing ls ~/folder/* | wc -l

O que faz:

  • Enviar entrada padrão para saída padrão
  • Executar argumento. $*retorna todos os argumentos como uma sequência. A saída desse comando vai para a saída padrão do script por padrão.

Portanto, o stdout do magicalUnionThing será o stdin + stdout do comando que é passado como argumento.

É claro que existem maneiras mais simples, como em outras respostas.
Talvez essa alternativa possa ser útil em alguns casos.

Rolf
fonte