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!