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-lpara imprimir apenas o nome do arquivo correspondente."$1"é citado novamente para considerar espaços em branco ou curingas.
- Localiza o nome de arquivo base do arquivo correspondente (
- E passa dois argumentos para
sh: "sh" e{}.shé destinado a$0do script embutido (o nome do programa em execução, que o shell usa para relatório de erros, por exemplo).findsubstituirá um argumento que consiste exatamente em{}com o nome do arquivo correspondente, portanto, o próprio scriptshobté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.