De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
Colaboração: Julio Cezar Neves
Data de Publicação: 15 de agosto de 2019
Eu quero listar todos os arquivos com os nomes começados por arq de um
diretório, contando-os, então eu faço:
ls arq* | while read Arq
do
echo $((i)) $Arq
done; echo Eu tenho :$i: arquivos
E a saída disso foi a seguinte:
1 arq
2 arq.err
3 arq.err1
4 arq.limpo
5 arq.lixo
6 arq.log
7 arq10
Eu tenho :: arquivos
Ou seja, funcionou tudo beleza, mas na hora de totalizar deu caca. Você sabe por quê? Explico: o while inteiro rodou num subshell provocado pelo pipe (|) e como você pode ver pela contagem, a variável $i foi incrementada, mas ao terminar o subshell, todo seu ambiente foi embora com ele e $i não escapou.
Como fazer então? Se fosse um arquivo que eu estivesse listando, bastaria redirecionar a entrada do loop com um < ARQUIVO logo após o done. Mas o ls não é um arquivo, é um comando, então o que fazer? É para isso que existe a substituição de processos, então você deveria fazer:
while read Arq
do
echo $((i)) $Arq
done < <(ls); echo Eu tenho :$i: arquivos
onde o 1º menor (<) é o redirecionamento de entrada (stdin) e o <(ls) é a Substituição de Processos.
Veja isso:
cat <(ls arq*)
arq
arq.err
arq.err1
arq.limpo
arq.lixo
arq.log
arq10
Se o cat listou é porque o <(ls arq*) é um arquivo. Então vamos inverter isso para que ver que arquivo é esse:

Disso tudo que vimos até agora, podemos resumir dizendo que a Substituição de Processos é uma técnica na qual se emula a saída de um comando como vindo de um arquivo temporário do tipo FIFO ou Named Pipe e serve para uso em instruções que precisam de um arquivo como entrada.
Como um exemplo final, vejamos o comando comm, muito pouco usado, porém excelente no que se propõe. Sua sintaxe é:
comm [OPCOES]... ARQ1 ARQ2
e ele é usado para comparar linha a linha ARQ1 e ARQ2, desde que estejam classificados
Sem nenhuma opção ele produz uma saída com 3 colunas:
ARQ1
ARQ2
Mas as suas principais opções são:
-1 |
Suprime a coluna 1 |
-2 |
Suprime a coluna 2 |
-3 |
Suprime a coluna 3 |
Uma vez entendido o comando, voltemos à Substituição de Parâmetros: o problema era excluir todos os arquivos que não tivessem uma determinada palavra. Veja o one-liner solução:
$ rm -i $(comm -13 <(grep -li 'PALAVRA' *) <(ls))
Nesta linha o comando rm -i removerá interativamente a saída do comando:
comm -13 <(grep -li 'PALAVRA' *) <(ls)
Que nada mais é que a instrução comm comparando 2 arquivos do tipo Named Pipe:
<(grep -li 'PALAVRA' *) |
Que devolve o nome (-l) de todos os arquivos que contêm PALAVRA e |
<(ls) |
O nome de todos os arquivos. |
Com a opção -13, são suprimidas as colunas 1 e 3, sobrando unicamente a coluna 2 que é referente às linhas únicas do arquivo 2, isto é, aqueles nos quais não foi encontrado PALAVRA.
Espero que este artigo tenha sido útil para entender o funcionamento da Substituição de Processos e, de quebra, os macetes do utilíssimo comando comm.