desenv-web-rp.com

Como posso enviar stdout para vários comandos?

Existem alguns comandos que filtram ou agem na entrada e, em seguida, transmitem-na como saída, acho que geralmente para stdout - mas alguns comandos apenas pegam o stdin e fazem o que fazem com ele e nada de saída.

Eu estou mais familiarizado com o OS X e, portanto, existem dois que vêm à mente imediatamente são 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 stdout e um arquivo, posso usar o comando tee. E eu sei um pouco sobre xargs, mas acho que não é isso que estou procurando.

Quero saber como posso dividir stdout para ir entre dois (ou mais) comandos. Por exemplo:

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

Provavelmente existe um exemplo melhor que esse, mas estou realmente interessado em saber como posso enviar stdout para um comando que não o retransmite e, ao mesmo tempo, mantendo stdout longe de ser "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ó consegui encontrar perguntas semelhantes que perguntavam 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.

Por fim, você pode perguntar "por que não fazer do 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 que eles processam a entrada?

Ah, e mais uma coisa - eu sei que poderia usar tee e um pipe nomeado (mkfifo), mas esperava uma maneira de fazer isso de forma concisa, concisa, sem uma configuração prévia: )

200
cwd

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

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

Isso enviará toda a saída de cat file.txt a pbcopy, e você obterá apenas 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
251
Mat

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 pipes e especificar cada um deles como uma saída de tee. Existem várias maneiras de fazer isso.

Substituição de processo

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

tee >(command1) >(command2) | command3

Descritores de arquivo

Em qualquer Shell POSIX, você pode usar vários descritores de arquivo explicitamente. Isso requer uma variante unix que suporte /dev/fd, já 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

Canais 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 pipes 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
133

Basta jogar com a substituição do processo.

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

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

17
Nikhil Mulley

Se você estiver usando zsh, poderá tirar proveito do poder do recurso MULTIOS, ou seja, se livrar do comando tee completamente:

uname >file1 >file2

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

Da mesma forma, o redirecionamento de entradas padrão

wc -l <file1 <file2

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

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

echo abc > >(grep -o a) > >(tr b x) > >(sed 's/c/y/')
17
jimmij

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

O script a seguir, por exemplo, pode 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
7
Sergiy Kolodyazhnyy

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)"
5
laebshade

Há também pee do pacote moreutils . É design para isso: pee 'command1' 'command2' 'cat -'

2
Xorax

Isso pode ser útil: http://www.spinellis.gr/sw/dgsh/ (gráfico direcionado Shell) Parece uma substituição do bash que suporta uma sintaxe mais fácil dos comandos "multipipe".

1
sivann

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

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

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

Você obtém um log completo e um filtrado.

0
Victor Sergienko