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.
- Localiza o nome de arquivo base do arquivo correspondente (
- 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 scriptsh
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.