Substituição e tubulação do processo

67

Eu queria saber como entender o seguinte:

Piping the stdout of a command into the stdin of another is a powerful technique. But, what if you need to pipe the stdout of multiple commands? This is where process substitution comes in.

Em outras palavras, pode processar a substituição do que o tubo pode fazer?

O que pode processar a substituição, mas o tubo não pode?

    
por Tim 21.07.2011 / 05:59

4 respostas

110

Uma boa maneira de aumentar a diferença entre eles é fazer uma pequena experiência na linha de comando. Apesar da semelhança visual no uso do caractere < , ele faz algo muito diferente de um redirecionamento ou pipe.

Vamos usar o comando date para testes.

$ date | cat
Thu Jul 21 12:39:18 EEST 2011

Este é um exemplo inútil, mas mostra que cat aceitou a saída de date em STDIN e cuspiu-a de volta. Os mesmos resultados podem ser obtidos por substituição de processo:

$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011

No entanto, o que aconteceu nos bastidores foi diferente. Em vez de receber um fluxo STDIN, cat recebeu o nome de um arquivo que precisava abrir e ler. Você pode ver esta etapa usando echo em vez de cat .

$ echo <(date)
/proc/self/fd/11

Quando cat recebeu o nome do arquivo, ele leu o conteúdo do arquivo para nós. Por outro lado, o echo apenas nos mostrou o nome do arquivo que foi passado. Essa diferença se torna mais óbvia se você adicionar mais substituições:

$ cat <(date) <(date) <(date)
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011

$ echo <(date) <(date) <(date)
/proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13

É possível combinar a substituição de processos (que gera um arquivo) e o redirecionamento de entrada (que conecta um arquivo a STDIN):

$ cat < <(date)
Thu Jul 21 12:46:22 EEST 2011

Parece praticamente o mesmo, mas desta vez o gato foi passado no fluxo STDIN em vez de um nome de arquivo. Você pode ver isso experimentando com echo:

$ echo < <(date)
<blank>

Como echo não lê STDIN e nenhum argumento foi passado, não conseguimos nada.

Pipes e redirecionamentos de entrada empurram o conteúdo para o fluxo STDIN. A substituição de processos executa os comandos, salva sua saída em um arquivo temporário especial e, em seguida, passa esse nome de arquivo no lugar do comando. Qualquer comando que você esteja usando, trata-o como um nome de arquivo. Observe que o arquivo criado não é um arquivo comum, mas um pipe nomeado que é removido automaticamente quando não é mais necessário.

    
por 21.07.2011 / 11:49
22

Suponho que você esteja falando sobre bash ou algum outro shell avançado, porque o shell posix não possui substituição de processo .

bash relatórios de páginas de manual:

Process Substitution
Process substitution is supported on systems that support named pipes (FIFOs) or the /dev/fd method of naming open files. It takes the form of <(list) or >(list). The process list is run with its input or output connected to a FIFO or some file in /dev/fd. The name of this file is passed as an argument to the current command as the result of the expansion. If the >(list) form is used, writing to the file will provide input for list. If the <(list) form is used, the file passed as an argument should be read to obtain the output of list.

When available, process substitution is performed simultaneously with parameter and variable expansion, command substitution, and arithmetic expansion.

Em outras palavras, e de um ponto de vista prático, você pode usar uma expressão como a seguinte

<(commands)

como nome de arquivo para outros comandos que requerem um arquivo como parâmetro. Ou você pode usar o redirecionamento para esse arquivo:

while read line; do something; done < <(commands)

Voltando à sua pergunta, parece-me que a substituição de processos e os tubos não têm muito em comum.

Se você deseja canalizar em sequência a saída de vários comandos, você pode usar um dos seguintes formulários:

(command1; command2) | command3
{ command1; command2; } | command3

mas você também pode usar o redirecionamento na substituição de processos

command3 < <(command1; command2)

finalmente, se command3 aceitar um parâmetro de arquivo (em substituição a stdin)

command3 <(command1; command2)
    
por 21.07.2011 / 10:22
18

Aqui estão três coisas que você pode fazer com a substituição do processo que, de outra forma, seriam impossíveis.

Entradas de processo múltiplas

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)

Simplesmente não há como fazer isso com canos.

Preservando STDIN

Digamos que você tenha o seguinte:

curl -o - http://example.com/script.sh
   #/bin/bash
   read LINE
   echo "You said ${LINE}!"

E você quer executá-lo diretamente. O seguinte falha miseravelmente. O Bash já está usando o STDIN para ler o script, então outras entradas são impossíveis.

curl -o - http://example.com/script.sh | bash 

Mas isso funciona perfeitamente.

bash <(curl -o - http://example.com/script.sh)

Substituição do processo de saída

Observe também que a substituição de processos funciona de outra maneira também. Então você pode fazer algo assim:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \
  '/Permission denied/ s/.*\(\/proc.*\):.*//p' > denied.txt )

Isso é um exemplo complicado, mas envia stdout para /dev/null , enquanto canaliza stderr para um script sed para extrair os nomes dos arquivos para os quais um erro "Permissão negada" foi exibido e, em seguida, envia os resultados do THOSE para um arquivo.

Note que o primeiro comando e o redirecionamento stdout estão entre parênteses ( subshell ) para que apenas o resultado do comando THAT seja enviado para /dev/null e não o faça t mexer com o resto da linha.

    
por 21.12.2011 / 20:52
9

Se um comando receber uma lista de arquivos como argumentos e processar esses arquivos como entrada (ou saída, mas não comumente), cada um desses arquivos pode ser um canal nomeado ou pseudo-arquivo / dev / fd fornecido de forma transparente por processo de subsituição :

$ sort -m <(command1) <(command2) <(command3)

Isto "canalizará" a saída dos três comandos para ordenar, já que o sort pode pegar uma lista de arquivos de entrada na linha de comando.

    
por 21.07.2011 / 10:20