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:
-xdev: não procura em diretórios localizados em outros sistemas de arquivo.
-mindepth
<n>: desce pelo menos n níveis abaixo do diretório especificado antes de procurar pelos arquivos.
-maxdepth
<n>: procura por arquivos que estão localizados no máximo a n níveis abaixo do diretório especificado.
-follow: seguir link simbólico caso ele aponte para diretórios. Por padrão, o find não segue links.
-daystart: quando utilizar testes baseados em tempo (veja abaixo), esta opção considera a data como o início do dia atual, ao invés do padrão (24 horas antes do horário atual).
Um critério pode ser um ou mais testes atômicos. Alguns testes úteis são:
-type
<tipo_de_arquivo>: busca por um determinado tipo de arquivo. tipo_de_arquivo pode ser um dos seguintes: f (arquivo comum), d (diretório), l
(link simbólico), s (socket), b
(arquivo de bloco), c (arquivo de caractere) ou
p (pipe nomeado).
-name
<padrão>: busca arquivos cujos nomes combinam com o padrão fornecido. Com esta opção, o padrão é tratado como os caracteres curinga (veja Seção 3, “Casamento de Padrões no Shell”).
-iname
<pattern>: igual ao -name, mas ignora a diferença entre letras maiúsculas e minúsculas.
-atime
<n>, -amin <n>: procura arquivos que foram acessados pela última vez há n dias atrás (-atime) ou n minutos atrás (-amin). Você também pode especificar <+n> ou <-n>, o que significa que a busca será feita para arquivos acessados no máximo ou no mínimo há n dias/miniutos atrás.
-anewer
<um_arquivo>: encontra arquivos que foram acessados mais recentemente do que um_arquivo.
-ctime
<n>, -cmin <n>,
-cnewer <arquivo>: o mesmo que -atime, -amin e -anewer, mas aplica ao último horário em que o conteúdo do arquivo foi modificado.
-regex
<padrão>: o mesmo que -name, mas o padrão padrão é tratado como uma expressão regular.
-iregex
<padrão>: o mesmo que -regex, mas ignorando letras a diferença entre letras maiúsculas e minúsculas.
Há muitos outros testes, veja find(1) para mais detalhes. Para combinar os testes, você pode usar:
<c1>
-a <c2>: verdadeiro se c1 e c2 são verdadeiros; -a é implícito, então você digitar <c1> <c2>
<c3> se você quer testar c1, c2 e c3.
<c1>
-o <c2>: verdadeiro se c1
ou c2 são verdadeiros, ou ambos. Note que -o possui uma precedência menor do que -a, então se você quiser encontrar arquivos que case com o padrão c1 ou c2 e também com o critério c3, você terá que utilizar parênteses: ( <c1> -o <c2> ) -a <c3>. Você deve escapar (desativar) os parênteses, pois senão eles serão interpretados pelo shell!
-not
<c1>: inverte o teste c1,
desta forma, -not <c1> é verdadeiro quando c1 é falso.
Finalmente, você pode especificar uma ação para cada arquivo encontrado. O mais utilizados são:
-print: apenas imprima o nome de cada arquivo na saída padrão. Esta é a ação padrão.
-ls: exiba na saída padrão o equivalente ao comando ls -ilds aplicado em cada um dos arquivos encontrados.
-exec
<linha_de_comando>: executa o comando
linha_de_comando em cada arquivo encontrado. A linha_de_comando deve terminar com um ;, que você deve escapar para que o shell não interprete-a. A posição do arquivo é marcada com {}. Veja os exemplos de utilização.
-ok
<comando>: o mesmo que -exec mas pede por uma confirmação para cada comando.
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!