Percorrendo arquivos com nomes com espaço no Linux

{lang: 'pt-BR'}

O pipe é uma das grandes invenções do Unix. Através dele podemos encadear processos e transformar pequenas utilitários em ferramentas capazes de realizar tarefas realmente complexas.

A idéia é muito simples: todo processo tem associado a ele um arquivo de entrada, um arquivo de saída e um arquivo de erro. A entrada padrão de um processo é o teclado, e a saída padrão e de erro é a tela (console). A grande engenhosidade do pipe é a capacidade de redirecionar a saída de um processo, que seria para tela, para a entrada de outro processo. Isto permite que comandos sejam encadeados para realizar uma tarefa mais sofisticada.

Além de podermos encadear processos, o shell (aí depende do shell) ainda nos oferece uma linguagem de programação, o ShellScript. O ShellScript permite criar variáveis, condicionais, loopings etc. Usarei o Bash como referência.

Vamos pensar num bastante comum: Você tem um projeto em um diretório cheio de arquivos e subdiretórios. Você deseja mudar a permissão de todos os arquivos com a extensão *.pdf.

Primeiramente, precisamos procurar pelos arquivos usando o comando find

$ find projeto/ -iname "*.pdf"
projeto/relatorios/relatorio.pdf
projeto/contratos/contrato servico.pdf
projeto/documentacao/auditoria.pdf
projeto/documentacao/especificacao de produtos.pdf

Em seguida, precisamos passar o resultado do comando find para o comando chmod, para mudarmos as permissões. Infelizmente o comando chmod não recebe dados da entrada padrão, portanto, iremos usar uma outra ferramenta, as aspas invertidas (ou crase). Este mecanismo permite que a saída de um processo seja usado como parâmetro na chamada de outro processo:

chmod 640 `find projeto/ -iname "*.pdf"`
chmod: cannot access 'projeto/contratos/contrato': No such file or director
chmod: cannot access 'servico.pdf': No such file or director
chmod: cannot access 'projeto/documentacao/especificacao': No such file or director
chmod: cannot access 'de': No such file or director
chmod: cannot access 'produtos.pdf': No such file or director

Perceberam a confusão que se deu quando os arquivos tem espaço nos nomes?. Ao invés de iterar para cada arquivo, o chmod iterou para cada parte de nome entre espaços.

Ao invés de usar a crase, podemos usar o while em combinação com o comando read, e esquecer as dores de cabeça. Tudo funciona perfeitamente.

find projeto/ -name "*.pdf" | while read f; do chmod 640 "$f"; done

O comando while irá iterar em cada nova entrada do comando read, e este, por sua vez, irá ler cada ‘linha’ da saída do comando find e gravar na variável f. A variável f pode ser usada com qualquer tipo de comando dentro do bloco “do … done”. Tudo se encaixa.

Eu uso essa estratégia para uma infinidade de coisas:

# Mudar as permissões apenas de diretórios
find . -type d | while read f; do chmod 775 "$f"; done

# Procurar um texto em apenas um tipo de arquivo
find . -iname "*.xml" | while read f; do grep "palavra" "$f"; done

# remover diretórios .svn de um projeto
find . -iname ".svn" -type d | while read d; do rm -rf "$d"; done

Seria possível usar o parâmetro -delete do comando find para excluir arquivos, mas este parâmetro não funcionaria com diretórios não vazios.

{lang: 'pt-BR'}