Como posso enviar stdout para vários comandos?

161

Existem alguns comandos que filtram ou atuam na entrada, e então passam como saída, eu acho que normalmente para stdout - mas alguns comandos apenas pegam o stdin e fazem o que fazem com ele, e a saída nada.

Estou mais familiarizado com o OS X e, portanto, há dois que vêm à mente imediatamente como pbcopy e pbpaste - que são meios de acessar a área de transferência do sistema.

De qualquer forma, eu sei que se eu quiser pegar o stdout e cuspir a saída para ir para o stdout e um arquivo, então eu posso usar o comando tee . E eu sei um pouco sobre xargs , mas não acho que seja isso que estou procurando.

Eu quero saber como posso dividir stdout entre dois (ou mais) comandos. Por exemplo:

cat file.txt | stdout-split -c1 pbcopy -c2 grep -i errors

Provavelmente, existe um exemplo melhor do que esse, mas estou realmente interessado em saber como posso enviar o stdout para um comando que não o retransmite e ao mesmo tempo manter o stdout "silenciado" - não estou perguntando sobre como cat um arquivo e grep parte dele e copiá-lo para a área de transferência - os comandos específicos não são tão importantes.

Além disso - não estou perguntando como enviar isso para um arquivo e stdout - isso pode ser uma pergunta "duplicada" (desculpe), mas fiz algumas pesquisas e só encontrei outras semelhantes que perguntavam sobre como dividido entre stdout e um arquivo - e as respostas para essas perguntas pareciam ser tee , o que eu acho que não funcionará para mim.

Finalmente, você pode perguntar "por que não apenas tornar o pbcopy a última coisa na cadeia de tubos?" e minha resposta é 1) e se eu quiser usá-lo e ainda ver a saída no console? 2) e se eu quiser usar dois comandos que não geram stdout depois de processar a entrada?

Ah, e mais uma coisa - eu percebi que eu poderia usar tee e um pipe nomeado ( mkfifo ), mas eu esperava que isso fosse feito inline, concisa, sem uma configuração anterior:)

    
por cwd 07.01.2012 / 04:32

8 respostas

212

Você pode usar tee e processar substituição por isso:

cat file.txt | tee >(pbcopy) | grep errors

Isso enviará toda a saída de cat file.txt para pbcopy e você só obterá o resultado de grep no seu console.

Você pode colocar vários processos na parte tee :

cat file.txt | tee >(pbcopy) >(do_stuff) >(do_more_stuff) | grep errors
    
por 07.01.2012 / 10:29
110

Você pode especificar vários nomes de arquivos para tee e, além disso, a saída padrão pode ser canalizada em um comando. Para despachar a saída para vários comandos, você precisa criar vários canais e especificar cada um deles como uma saída de tee . Existem várias maneiras de fazer isso.

Substituição de processos

Se o seu shell é ksh93, bash ou zsh, você pode usar a substituição do processo. Esta é uma maneira de passar um pipe para um comando que espera um nome de arquivo. O shell cria o pipe e passa um nome de arquivo como /dev/fd/3 para o comando. O número é o descritor de arquivo ao qual o pipe está conectado. Algumas variantes unix não suportam /dev/fd ; Nesses, um pipe nomeado é usado no lugar (veja abaixo).

tee >(command1) >(command2) | command3

Descritores de arquivos

Em qualquer shell POSIX, você pode usar vários descritores de arquivos explicitamente. Isso requer uma variante unix que suporte /dev/fd , uma vez que todas, exceto uma das saídas de tee , devem ser especificadas pelo nome.

{ { { tee /dev/fd/3 /dev/fd/4 | command1 >&9;
    } 3>&1 | command2 >&9;
  } 4>&1 | command3 >&9;
} 9>&1

pipes nomeados

O método mais básico e portátil é usar pipes nomeados . A desvantagem é que você precisa encontrar um diretório gravável, criar os canais e limpar depois.

tmp_dir=$(mktemp -d)
mkfifo "$tmp_dir/f1" "$tmp_dir/f2"
command1 <"$tmp_dir/f1" & pid1=$!
command2 <"$tmp_dir/f2" & pid2=$!
tee "$tmp_dir/f1" "$tmp_dir/f2" | command3
rm -rf "$tmp_dir"
wait $pid1 $pid2
    
por 20.07.2012 / 00:38
15

Apenas brinque com a substituição do processo.

mycommand_exec |tee >(grep ook > ook.txt) >(grep eek > eek.txt)

grep são dois binários que possuem a mesma saída de mycommand_exec que a entrada específica do processo.

    
por 07.01.2012 / 13:59
15

Se você estiver usando zsh , poderá aproveitar o poder do recurso MULTIOS , ou seja, eliminar completamente o comando tee :

uname >file1 >file2

apenas gravará a saída de uname em dois arquivos diferentes: file1 e file2 , o que equivale a uname | tee file1 >file2

Semelhantemente, redirecionamento de entradas padrão

wc -l <file1 <file2

é equivalente a cat file1 file2 | wc -l (por favor, note que isto não é o mesmo que wc -l file1 file2 , o número posterior de linhas em cada arquivo separadamente).

É claro que você também pode usar MULTIOS para redirecionar a saída para arquivos, mas para outros processos, usando a substituição de processos, por exemplo:

echo abc > >(grep -o a) > >(tr b x) > >(sed 's/c/y/')
    
por 22.12.2014 / 02:24
6

Para uma saída razoavelmente pequena produzida por um comando, podemos redirecionar a saída para o arquivo temporário e enviar esse arquivo temporário para os comandos em loop. Isso pode ser útil quando a ordem dos comandos executados pode ser importante.

O script a seguir, por exemplo, poderia fazer isso:

#!/bin/sh

temp=$( mktemp )
cat /dev/stdin > "$temp"

for arg
do
    eval "$arg" < "$temp"
done
rm "$temp"

Teste executado no Ubuntu 16.04 com /bin/sh como dash shell:

$ cat /etc/passwd | ./multiple_pipes.sh  'wc -l'  'grep "root"'                                                          
48
root:x:0:0:root:/root:/bin/bash
    
por 05.02.2017 / 20:38
5

Capture o comando STDOUT para uma variável e reutilize-o quantas vezes quiser:

commandoutput="$(command-to-run)"
echo "$commandoutput" | grep -i errors
echo "$commandoutput" | pbcopy

Se você precisar capturar STDERR também, use 2>&1 no final do comando, assim:

commandoutput="$(command-to-run 2>&1)"
    
por 07.01.2012 / 04:43
0

Isso pode ser útil: link (shell de gráfico direcionado) Parece uma substituição de bash que suporta uma sintaxe mais fácil para comandos "multipipe".

    
por 13.12.2016 / 19:14
0

Aqui está uma solução parcial rápida e suja, compatível com qualquer shell, incluindo busybox .

O problema mais restrito que ele resolve é: imprimir o stdout completo em um console e filtrá-lo em outro, sem arquivos temporários ou pipes nomeados.

  • Inicie outra sessão no mesmo host. Para descobrir seu nome TTY, digite tty . Vamos supor /dev/pty/2 .
  • Na primeira sessão, execute the_program | tee /dev/pty/2 | grep ImportantLog:

Você recebe um log completo e um log filtrado.

    
por 15.03.2018 / 00:53