Arquivos de texto contendo seu próprio nome

4

Eu preciso encontrar em determinado diretório e todos os seus subdiretórios todos os arquivos de texto que contenham seu próprio nome. Como eu faço isso? (preferencialmente sem comando awk)

    
por vikiv 31.10.2014 / 08:28

4 respostas

6

Para ser maximamente portável e funcionar quando nomes de arquivos podem incluir espaços ou outros caracteres de IFS , e em sistemas onde find não manipula {} dentro de aspas, use find -exec em combinação com sh e passagem de parâmetro comum:

find DIRECTORY -type f -exec sh -c 'grep -lFe "$(basename "$1")" "$1"' sh {} ';'

find DIRECTORY -type f enumera todos os arquivos comuns em DIRECTORY recursivamente. -exec ... ';' executa um comando para cada arquivo encontrado. A carne dele é o comando sh até {} , que:

  • Executa o shell padrão com uma string de comando script grep -lFe "$(basename "$1")" "$1" para cada arquivo correspondente, qual:
    • Localiza o nome de arquivo base do arquivo correspondente ( $1 ) usando substituição de comando e basename - tanto a substituição do comando quanto a variável são citadas para explicar os casos em que pode conter espaços em branco ou curingas; e
    • grep é o arquivo correspondente (ainda " $1 ") para esse nome de base, usando -l para imprimir apenas o nome do arquivo correspondente. "$1" é citado novamente para considerar espaços em branco ou curingas.
  • E passa dois argumentos para sh : " sh " e {} . sh é destinado a $0 do script embutido (o nome do programa em execução, que o shell usa para relatório de erros, por exemplo). find substituirá um argumento que consiste exatamente em {} com o nome do arquivo correspondente, portanto, o próprio script sh obtém um único argumento com o caminho completo do arquivo disponível como $1 .

Isso funcionará em qualquer sistema compatível com POSIX. Usando {} dentro de uma string entre aspas com find -exec comportamento definido pela implementação , e por isso não é portável (embora seja suportado pelo GNU grep e pela maioria dos BSDs) e é perigoso, pois o nome do arquivo é interpretado como código do shell .

Os nomes de arquivos arbitrários sempre precisam ser citados quando podem conter espaço em branco, caracteres curinga ou o valor da variável do separador de campo de entrada IFS é desconhecido; caso contrário, eles serão divididos em várias palavras e farão com que o programa se comporte de maneiras inesperadas.

Com mais eficiência, você pode usar find -exec com + para executar o número mínimo de shells possíveis:

find DIRECTORY -type f -exec sh -c 'for x in "$@" ; do grep -lFe "$(basename "$x")" "$x" ; done' sh {} '+'

Isso ainda é portável, mas fornece muitos caminhos de arquivo para cada execução de sh , para que não inicie o mesmo número de shells. Dentro do programa shell há um for loop que analisa cada um dos arquivos. Se isso é algo que vai ser executado com freqüência, esta é uma opção melhor do que usar " ; " para finalizar o comando.

    
por 31.10.2014 / 09:23
1
find directoryname -type f -exec grep -l {} {} \;

Isso diz ao find para procurar por arquivos simples (ou seja, excluindo diretórios, dispositivos, links simbólicos, etc.) e para cada arquivo encontrado executar grep com os parâmetros fornecidos. -l instrui o grep a mostrar apenas o nome do arquivo, não a linha correspondente. O {} representa o nome do arquivo, \; finaliza o comando.

É claro que você provavelmente quis dizer que o nome do arquivo e não o caminho deve ser procurado. Isso significa que a parte do diretório precisa ser removida da primeira parte {} . Uma solução poderia ser:

find directoryname -type f -exec sh -c 'grep -l $(basename {}) {}' \;

Para cada arquivo agora, um shell é iniciado, pois a substituição de parâmetro com $( .. ) deve ser feita. basename retira qualquer parte do diretório principal.

Nota aspas simples precisam ser usadas, caso contrário, a parte $ (..) será avaliada quando você digitar o comando, não quando o shell for iniciado por find.

    
por 31.10.2014 / 08:50
1

Em um sistema GNU, você pode fazer:

find . -type f -printf '%f
find . -type f -printf '%f%pre%%p%pre%' | xargs -r0n2 grep -lFe
%p%pre%' | xargs -r0n2 grep -lFe

( -type f é restrito apenas a arquivos regulares . Altere para -xtype f se você também quiser considerar links simbólicos para arquivos regulares).

    
por 31.10.2014 / 12:44
0

Isso funcionou para mim:

find /path/ -type f -exec sh -c "grep -l \"\$(basename {})\" {}" \;

Explanation:
  • -type f : encontrar apenas arquivos
  • sh -c : executa um shell para cada arquivo
  • grep -l : mostra apenas os nomes dos arquivos
  • \$(basename {}) : grep para o nome de base
por 31.10.2014 / 08:52