Tenho usado o comando find há décadas, mas nunca o entendi realmente – até agora. Forçar-me a seguir em frente e finalmente quebrar esse comando evasivo foi a chave para o sucesso.
Apresentando o comando find
O comando find é um dos programas Linux mais estranhos que você usará. É essencial o suficiente para ser onipresente, mas obscuro o suficiente para não ser o favorito de ninguém. Existem alternativas como fd, mas find é o mínimo denominador comum que funciona, praticamente.
No caso mais simples, usar find é muito fácil:
find . -name '*.txt'
Esse tipo comum de consulta encontra todos os arquivos no diretório atual ou em qualquer diretório abaixo dele que termine com “.txt”. Se isso é tudo que você já conseguiu, provavelmente não sabe o quão complexo é o achado. Sua página de manual tem 1.639 linhas (versão 4.9.0) e, uma vez que você se aprofunda no find, ele começa a parecer menos um utilitário útil e mais uma linguagem de programação completa.
Essa complexidade sempre me desanimou no passado. Há muito tempo eu queria uma maneira simples de encontrar arquivos que editei recentemente, mas isso sempre me escapou. Os resultados acabam prejudicados por arquivos .git ou como uma bagunça indiferenciada. Então decidi sentar e finalmente entender o find; o resultado foi refrescante.
O uso básico de find é assim:
find [starting-point...] [expression]
Find então segue estas etapas para realizar seu trabalho:
- Processe cada arquivo e diretório dentro do ponto inicial; vários pontos de partida são suportados.
- Aplicar testes; se o arquivo passar por todos eles, tome uma atitude.
- Agir: a ação padrão é -imprimirque imprime o nome completo do arquivo.
Um simples nu encontrarsem argumentos, lista tudo abaixo do diretório atual:
Isso pode ser bastante útil por si só, mas o verdadeiro poder da descoberta vem com os testes e ações que ela suporta.
Obtendo find para relatar arquivos alterados recentemente
Quando você está trabalhando em uma tarefa mais complexa como essa, é uma boa ideia planejar o que você precisa e, em seguida, descobrir como alcançá-lo aos poucos. Para o comportamento que procuro, sei que preciso:
- Arquivos que possuo.
- Arquivos que não estão em diretórios ocultos.
- Resultados classificados por data, com as datas mostradas.
Também é útil ter alguns dados de teste para verificar se seu comando faz exatamente o que você deseja. Neste caso, estou usando a seguinte estrutura de arquivos:
Encontre arquivos de propriedade de um usuário
A primeira tarefa é simples, graças ao find’s -usuário test, que só corresponde a um arquivo se o usuário atual o possuir:
find . -user "${USER}"
USER é uma variável de ambiente que contém o nome de usuário do usuário atual.
Se você executar o procedimento acima, poderá notar que a saída contém diretórios:
Para garantir que a lista final contenha apenas arquivos, use find’s -tipo teste. É necessário um único caractere para indicar o tipo de arquivo correspondente: f para um arquivo normal, d para um diretório e l para um link simbólico, entre outros:
find . -user "${USER}" -type f
Neste ponto, find está reportando todos os arquivos – não diretórios – pertencentes ao usuário atual:
Encontre arquivos que não estão em diretórios ocultos
A próxima parte da tarefa é parar de localizar arquivos de relatórios em diretórios ocultos. A ação -prune diz ao find para não descer para um diretório correspondente:
find . -path "*/.*/*" -prune
Os resultados deste comando podem parecer estranhos à primeira vista, quase o oposto do que queremos:
Isso ocorre devido à maneira como a ação de poda se comporta, então aqui está o que está acontecendo:
- O teste -path corresponde a todos os arquivos (incluindo diretórios) com um caminho que inclui pelo menos um diretório oculto.
- A ação -prune impede que find entre nesses diretórios.
- Como não há outra ação além de -prune, find aplica a ação -print por padrão. Este comando é equivalente a
find . -path "*/.*/*" -prune -print.
Em vez de imprimir os diretórios ocultos que foram removidos, você pode imprimir todo o resto usando -o:
find . -path "*/.*/*" -prune -o -print
O operador -o é um OR lógico, o que significa que find corresponderá à expressão anterior ou à seguinte. Portanto, se um caminho tiver um diretório oculto, ele será removido; caso contrário, será impresso (uma expressão sem teste sempre corresponderá).
Neste ponto, você pode mesclar a expressão original filtrada por proprietário e tipo de arquivo:
find . -path "*/.*/*" -prune -o -user "${USER}" -type f -print
Este comando find localiza todos os arquivos necessários e nada mais:
O que resta é classificá-los pelos modificados mais recentemente e exibir essa data ao lado de cada arquivo.
Você pode usar o teste -path para obter os mesmos resultados, com um comando um pouco mais curto:
find . -type f -not -path '*/.*/*' -user "${USER}"
Se você tentar isso, obterá os mesmos resultados da versão que usa -prune. A diferença é que -prune é mais eficiente porque evita testar arquivos em diretórios ocultos. Se estiver trabalhando com muitos diretórios, você deve sempre usar -prune para pular qualquer um que possa evitar.
Classifique os resultados e imprima bem
O comando find não lida com a classificação em si; para esta tarefa, você pode usar sortum dos comandos essenciais de processamento de texto. Porém, primeiro você precisará da data de modificação para cada arquivo. A ação -printf informa exatamente como você deseja sua saída:
find . -path "*/.*/*" -prune -o -user "${USER}" -type f -printf "%TY-%Tm-%Td %pn"
A ação -printf usará o conteúdo da string entre aspas para formatar as informações do arquivo. Neste exemplo, %TY refere-se ao ano da última modificação. %Tm e %Td são o mês e o dia, respectivamente, enquanto %p é o nome do arquivo.
Você pode especificar uma data mais granular para uma classificação mais precisa, mas só estou realmente interessado em datas, então a etapa final é canalizar para classificar. Observe que estou ordenando cada linha com a data primeiro. Isso tem dois propósitos: mantém tudo bem alinhado, mas também torna a classificação uma tarefa trivial. Basta canalizar os resultados de find para sort:
find . -path "*/.*/*" -prune -o -user "${USER}" -type f -printf "%TY-%Tm-%Td %pn" | sort