Jump to content

Após ver sobre o comando find no nosso canal Papo Binário decidi estudar um pouco mais sobre o mesmo. Revisando estas anotações pensei que seria interessante compartilhá-las, tendo em vista que o find é um comando extremamente poderoso. Alguns dos parâmetros já foram abordados no vídeo, mas vou repassar alguns aqui, não custa nada, não é mesmo?!

Este comando pode ser útil para diversas tarefas, dentre elas investigação, administração ou mesmo aprendizado sobre o sistema.

Indo direto ao ponto, o find é um comando para procurar itens no filesystem (arquivos, links, diretórios, etc). O que o difere de outros programas que fazem isto é a quantidade de opções que a ferramenta possui e o fato de não depender da variável $PATH para encontrar um binário. O comando leva como principal parâmetro um path, ou seja, um caminho para procurar algo. Se não passarmos nada ele entenderá que o path é o diretório atual:

find 
find /etc

Se não especificarmos exatamente o que queremos buscar o find simplesmente nos mostra tudo o que achar pois ele varre o filesystem recursivamente na hora de procurar algo, mas não queremos isso tudo, até porque não seria muito útil. ?

Vamos tentar entender alguns filtros interessantes... Imagine que você é um administrador e precisa verificar todos os arquivos que pertencem a um usuário em específico:

find / -type f -user leandro 

O que fizemos aqui? Utilizamos 2 tipos de filtros, um deles foi o -user, que busca arquivos que pertencem apenas à aquele usuário. O -type filtra pelo tipo de item no filesystem e suporta os seguintes tipos:

  • d -> diretório
  • f -> arquivo regular
  • l -> link simbólico
  • s -> socket
 
 
Procurando por arquivos perdidos:
 
Imagine agora que seu sistema está uma bagunça e você não faz ideia onde está um arquivo em específico, pense que você tem no mínimo 8 subdiretórios lotados de arquivos e você não lembra onde está o que você está procurando, só lembra que existe a palavra "mentebinaria" no nome dele. Além disso, você também sabe que não está nos primeiros 2 subdiretórios. Podemos resolver com:
find . -mindepth 2 -name "*mentebinaria*" -type f
A primeira coisa que fizemos foi utilizar a opção -mindepth, que especifica quantos níveis na hierarquia o find deve olhar no mínimo (a opção -maxdepth especifica o máximo). A outra opção foi a -name, que procura por um nome completo ou parte dele como fizemos no exemplo utilizando o wildcard * (asterisco) para bater com qualquer string antes de depois da palavra "mentebinaria".
 

Executando comandos:

Na minha opinião uma das opções mais interessantes do find é a -exec, que praticamente executa comandos em cima do que o find encontrar. Não entendeu? Vamos lá... supondo que queiramos ver qual o tipo de arquivo de todos os arquivo que encontrarmos em um diretório em específico com o comando file:

find . -type f -exec file {} \;

Temos muita coisa pra entender nesta linha. Primeiro, o -exec trabalha com o conceito de targets (as chaves {} ) e isto significa: coloque tudo o que o find devolver no local da chave. Para cada arquivo que o find achar ele rodará o comando file naquele arquivo. Incrível, não?

Sim, mas com isto estaremos executanto o mesmo comandos múltiplas vezes, por exemplo:

leandro@teste:~$ find . -type f | wc -l
295

Imagine rodar isto 295 vezes, muita coisa, não? Se notarmos no primeiro exemplo do -exec vemos que no fim da linha tem um ponto de vírgula e este indica o fim do -exec para o find (e não para o shell). Temos que usar a contra barra para escapar e o shell não pensar que é para ele.

Ok, mas até agora não vimos como melhorar isto. Concordam que o comando file aceita mais de um parâmetro?

file arq1 arq2 arq3

E se pudéssemos pegar tudo que o find achar e, ao invés de rodar um comando do -exec por vez passamos tudo um atrás do outro? É exatamente isto o que o + faz e para ele não precisamos escapar:

find . -type f -exec file {} +

Este exemplo é a mesma coisa do anterior, mas de forma mais automatizada. Vamos medir a velocidade dos dois comandos:

root@teste:~# time find / -type l -exec file {} \;

...

real    0m15,127s
user    0m0,336s
sys     0m1,640s
root@teste:~# time find / -type l -exec file {} +

...

real    0m1,119s
user    0m0,212s
sys     0m0,396s

Bem mais rápido com o +, não acham? ?

 

Investigando o sistema:

Seu servidor foi atacado, você não sabe exatamente o que aconteceu e como aconteceu, só sabe que nem tudo está funcionando do jeito que deveria. Uma coisa interessante à se fazer é tentar olhar para o que exatamente foi alterado desde o ataque. Imagine que isto ocorreu à 2 dias:
find / -mtime -2

Aqui estamos dizendo que a partir da hora que rodarmos o comando olhar para tudo que foi modificado 48 horas atrás. Podemos também verificar se algo foi acessado com -atime.

E se você não sabe exatamente quando foi o ataque? A única coisa que você sabe é que a última coisa que você fez foi adicionar novas funcionalidades à um script que você tem. Podemos procurar por tudo que foi modificado após este arquivo com a opção -newer:

find /etc -newer <arquivo_velho>

Mas como isto? O Linux guarda um tipo de informação chamada MAC no inode de cada arquivo, resumindo é simplesmente a data da última modificação, acesso e criação do arquivo ao qual aquele inode se refere. Apenas como curiosidade, o comando stat lê essas informações também. ?

 

Mais algumas informações:

Ok, agora você não teve nenhum problema, só quer algumas informações sobre os arquivos que o find encontrar. A opção -size <n> pode ajudar a procurar por arquivos maiores (+) ou menores (-) que o especificado:

find /var -size +20k

Podemos trabalhar com os seguintes formatos:

  • c -> bytes
  • k -> KB
  • 0 ou -empty -> vazio
find . -empty

Não está satisfeito? Ok, a opção -ls ti da muito mais informações (praticamente aplica um ls -lids em cima de tudo que o find achar)

find . -user leandro -type d -ls 

 

Facilitando o parsing:

Achou as opções de informações fracas? De fato a saída fica bem poluída. E se você precisasse todo dia monitorar informações específicas sobre arquivos específicos e criasse um script para isso, como você faria para obter estas informações? O find ti ajuda nisso também!!! Se você está familiarizado com a linguagem C (se não está veja isto) a função printf do C pode imprimir uma saída formatada de acordo com o que você escolher (string, inteiro, inteiro sem sinal, etc).

Assim como em C, a opção -printf possui uma série de diretivas para formatarmos a saída do find como quisermos, algumas delas são:

  • %f -> nome do arquivo
  • %p -> path completo
  • %i -> inode
  • %M -> permissões
  • %n -> número de hard links
find / -type f -atime -1 -printf '%p %i %M \n'

O único detalhe aqui é que por padrão o -printf não coloca um caractere de nova linha, devemos adicionar como no exemplo. Com isto a saída fica bem mais interesante para um script ler, não acham?! Aqui está o exemplo de uma saída:

file1 262295 -rw-r--r--
file2 262283 -rw-r--r--
file3 262296 -rw-r--r--

Estas foram algumas dicas sobre o comando find. Com certeza informações mais completas podem ser encontradas no manual do comando, este tutorial tem como objetivo simplesmente compartilhar minhas anotações sobre o que acho bem interessante e usual sobre o comando find.

Qualquer dúvida, crítica ou sugestão, por favor, sinta-se à vontade para comentar e obrigado! ?


Revisão: Fernando Mercês

User Feedback

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
  • This will not be shown to other users.
  • Add a review...
Guest

Guest

More options...
   4 of 4 members found this review helpful 4 / 4 members

Cobre o básico do uso de find muito bem. Eu acrescentaria um detalhe:

É possível criar expressões mais complexas com o uso de \( e \) e as opções -and (que é implícita) ou -or. Por exemplo:

$ find ~/videos/ -type f \( -name '*.webm' -or -name '*.avi' \) \
  -exec convert2mp4.sh '{}' \;

Aqui todos os arquivos encontrador a partir do diretório ~/videos/, nomeados *.webm ou *.avi, serão passados para o script convert2mp4.sh.

Notar que, assim como o ';' final é "escapado", os parênteses também tém que sê-lo.