Substituição de processos no Bash

proc_subst-1_abertura

Um recurso muito útil do Bash, e também de outras shells em sistemas UNIX, é o pipe¹ e com ele é possível direcionar a saída de um programa para a entrada de outro. A substituição de processos — ou process substitution — funciona de modo parecido porém aqui é possível se direcionar a saída de mais de um programa para a entrada de outro como se este fossem arquivos.

(¹) Sim, outros sistemas não UNIX também possuem este recurso mas implementado de modo bem diferente as vezes.

Antes de começar

Bom deixar claro há uma diferença entre a substituição de comandos e a substituição de processos. Na primeira a saída padrão de um comando — realizada através de `«comando»` ou  $(«comando») — é convertida em uma sequência de caracteres para ser tanto impressa como atribuída a uma variável:

$ echo "$( uname -v )"
#119-Ubuntu SMP Tue Sep 12 14:59:54 UTC 2017

ou,

$ version=$( uname -v )
$ echo "${version}"
#119-Ubuntu SMP Tue Sep 12 14:59:54 UTC 2017

Na substituição de processos — realizada através de <(«comando») para entrada e >(«comando») para saída — a saída de um comando é passado para outro como se este fosse um arquivo:

$ cat <( uname -v )
#119-Ubuntu SMP Tue Sep 12 14:59:54 UTC 2017

Para ajudar a fixar, pense no <(...) e no >(...)  como se fossem os nomes dos arquivos que serão, respectivamente, lido e gravado.

Arquivos de entrada

Então é só um pipe diferente? Lembra da parte de redirecionar a saída de mais de um programa? Que tal juntar e compactar a saída de diversos programas?

$ gzip -c <( uname -a ) <( lscpu ) > host.gz

Para o gzip estes são uma lista de arquivos  para compactar, jogar na saída padrão e então gravar em disco sob o nome de “host.gz”.

Ou extrair das últimas 10 linhas de um arquivo as 5 primeiras (sim, é possível colocar uma substituição dentro da outra:

$ head --lines=5 <( tail --lines=10 <( lspci ) )

Um outro uso interessante é redirecionar a saída de um programa que, normalmente, não aceitaria recebê-lo diretamente através do pipe².

$ vim <( lspci )

E nesta mesma lógica, com um exemplo mais útil, habilitando a edição de um dataset arquivo codificado em EBCDIC sem necessariamente criar um arquivo temporário:

$ echo "HELLO WORLD FROM 1969" | iconv -t EBCDICUS > DATASET.TEXT
$ vim <( iconv -f EBCDICUS < DATASET.TEXT )

E usando o comando “:file” dentro do Vim é possível descobrir que o nome do arquivo que se está editando, na verdade ele é um file descriptor criado pelo próprio Bash para “interligar” or programas e, sim, é um arquivo temporário.

(²) Tente usar “lspci | vim” e veja como não irá funcionar.

Arquivos de saída

Um exemplo um pouco diferente utilizando a substituição de processos para baixar um arquivo via wget e ao mesmo tempo compactá-lo com o gzip.

$ wget http://ftp.ubuntu.com/ubuntu/dists/yakkety/Release \
  --output-document=>( gzip -c -9 > Release.gz )

Ou enquanto é baixado, filtrar o conteúdo do arquivo (no caso remover as chaves MD5 dele) antes de comprimi-lo:

$ wget http://ftp.ubuntu.com/ubuntu/dists/yakkety/Release \
  --output-document=>( egrep -v "^ [0-9a-f]{32} " |\
  gzip -c -9 > Release_no_md5.gz )

Em ambos os casos o wget não faz ideia do que está acontecendo dentro da substituição (ou mesmo que ela está ocorrendo), para ele é apenas um arquivo comum onde ele está salvando o que está sendo baixado.

Que tal alguma concorrência?

E para finalizar, usando o vimdiff³ para visualizar as diferenças entre dos arquivos que não estão em um servidor externo:

$ export host='http://ftp.ubuntu.com/ubuntu/dists';\
  vimdiff <( curl ${host}/yakkety/Release ) \
          <( curl ${host}/zesty/Release )

O interessante deste exemplo, além de dispensar a criação de arquivos temporários, está no fato de que os arquivos são baixados simultaneamente já que cada curl roda em uma subshell.

(³) E para quem não conhece, é um modo de operação do Vim em que ele exibe as diferenças entre dois arquivos.

Concluindo

A substituição de processos é um dos recursos mais interessantes do Bash. Apesar da funcionalidade ser parecida com a do pipe, ela acaba sendo útil para os casos onde não é possível utilizá-lo e, claro, permitir a implementação concorrência (multitarefa) de forma transparente dentro dos seus scripts — beneficiando ações transferência de arquivos (cópia, download, upload etc), compressão, descompressão, validação e outras que podem ser executadas em paralelo.

Anúncios

2 comentários sobre “Substituição de processos no Bash

Os comentários estão desativados.