2. find: Encontrando Arquivos

O find é um utilitário de longa data no UNIX®. Seu papel é o de procurar recursivamente u ou mais dirtórios e arquivos que atendam a um certo conjunto de critérios. Embora ele seja muito útil, possui uma sintaxe um tanto obscura, e a sua utilização requer um pouco de prática. A sintaxe geral é:

find [opções] [diretórios] [critério1] ... [critérioN] [ação]

Se você não especificar qualquer diretório, o find irá buscar no diretório atual. Se você não especificar um critério, isto é o equivalente a dizer que todos os arquivos devem ser encontrados. As opções, critérios e ações são tão numerosas que nós vamos mencionar apenas algumas. Estas são algumas opções:

Um critério pode ser um ou mais testes atômicos. Alguns testes úteis são:

Há muitos outros testes, veja find(1) para mais detalhes. Para combinar os testes, você pode usar:

Finalmente, você pode especificar uma ação para cada arquivo encontrado. O mais utilizados são:

A melhor forma de consolidar todas as opções e parâmetros é com alguns exemplos. Para encontrar todos os diretórios em /usr/share, por exemplo, digitaríamos:

find /usr/share -type d

Vamos supor que você tenha um servidor HTTP. Todos os seus arquivos HTML estão em /var/www/html, que é também o diretório onde você está posicionado no momento. Você quer encontrar todos os arquivos que no tiveram os seus conteúdos modificados por um mês. E como você tem páginas de vários autores, alguns arquivos possuem a extensão html e outros a extensão htm. E voce quer criar um link para estes arquivos no diretório /var/www/obsolete. Você poderia digitar[27]:

find \( -name "*.htm" -o -name "*.html" \) -a -ctime -30 \
-exec ln {} /var/www/obsolete \;

Este é um exemplo um tanto complexo, e requer uma pequena explicação. O critério é o seguinte:

\( -name "*.htm" -o -name "*.html" \) -a -ctime -30

que faz o que queremos: ele encontra todos os arquivos cujos nomes terminam em .htm ou .html (“ \( -name "*.htm" -o -name "*.html" \)”), e (-a) que não foram modificados nos últimos 30 dias, o que é aproximadamente um mês(-ctime -30). Note os parênteses: eles são necessários aqui porque o -a tem uma precedência mais alta. Se não tiver nenhum parêntese, seriam encontrados todos os arquivos que terminam com .htm, mais todos os arquivos .html que não foram modificados no último mês, e isso não é o que esperávamos. Note também que os parênteses fora escapados: se nós tivéssemos utilizado ( .. ) ao invés de \( .. \), o shell teria interpretado-os e tentado executar -name "*.htm" -o -name "*.html" em um sub-shell... Outra solução seria colocar os parênteses entre aspas, mas uma contrabarra aqui é preferível, já que temos que isolar apenas um caractere.

E finalmente, temos o comando que deverá ser executado para cada arquivo:

-exec ln {} /var/www/obsolete \;

Aqui você também tem que escapar o caractere ;. Caso contrário o shell iria interpretá-lo como um separador de comando. Se acontecer de você esquecer, o find irá reclamar que está faltando um argumento para o -exec.

Um último exemplo: você tem um diretório enorme (/shared/images) que contém todo tipo de imagens. Você normalmente usa o comando touch para atualizar o horário de um arquivo chamado stamp neste diretório, e assim você tem uma referência de tempo. Você quer encontrar todos os arquivos de imagem JPEG que são mais novos do que o arquivo stamp, mas como você obtém arquivos de várias fontes, estes arquivos possuem as extensões jpg, jpeg, JPG ou JPEG. Você também quer evitar a busca no diretório old e que a lista de arquivos seja enviada por e-mail para vocês, e seu nome de usuário é usuario2:

find /shared/images -cnewer     \
     /shared/images/stamp       \
     -a -iregex ".*\.jpe?g"     \
     -a -not -regex ".*/old/.*" \
       | mail usuario2 -s "New images"

É claro que este comando não é muito útil se você tem que digitá-lo sempre, pois você gostaria que ele fosse executado regularmente. Uma maneira simples de ter o comando sendo executado periodicamente é através do uso do cron, como terá sua explicação mostrado a seguir.



[27] Note que este exemplo requer que /var/www e /var/www/obsolete estejam no mesmo sistema de arquivo!