Filtra os arquivos gerados por 'find' pela saída analisada do comando 'file'

3

Estou escrevendo uma ferramenta rápida para inspecionar o conteúdo de uma pasta node.js node_modules ou python virtualenv para dependências nativas. Como uma rápida primeira aproximação a isso, escrevi o seguinte comando.

find . | xargs file | awk '/C source/ {print $1} /ELF/ {print $1}'

Estou bem com os falsos positivos, mas não com os falsos negativos (por exemplo, arquivos contendo literalmente a string ELF ou C source podem ser marcados como suspeitos.), mas esse script também quebra potencialmente nomes de arquivos longos (porque xargs irá dividi-los) e nomes de arquivos contendo espaços (porque o awk será dividido em espaços em branco) e nomes de arquivos contendo novas linhas (porque o find usa novas linhas para separar caminhos).

Existe uma maneira de filtrar os caminhos gerados por find vendo se a saída de file {} (possivelmente com algumas opções adicionais para remover o caminho inteiramente da saída de file ) corresponde a uma determinada expressão regular?

    
por Gregory Nisbet 06.04.2016 / 20:58

2 respostas

2

O fator chave para alcançar find enlightenment;) é:

find's business is evaluating expressions -- not locating files. Yes, find certainly locates files; but that's really just a side effect.

--Unix Power Tools

Existe uma abordagem alternativa a essa questão que vale a pena conhecer (como também descrito em Unix Power Tools, na seção " Usando -exec para criar testes personalizados "):

find . -type f -exec sh -c 'file -b "$1" | grep -iqE "^ELF|^C source"' sh {} \; -print

Vale a pena conhecer esse método de filtragem, já que ele pode ser usado para muitas outras coisas além de simplesmente imprimir o nome do arquivo; apenas mude o operador -print para qualquer outro operador que você gostar (incluindo outro operador -exec ) e faça o que quiser com ele.

Existe uma desvantagem de desempenho para este comando (que também está presente em a outra resposta ), que é que, como estamos usando \; e não + , estamos gerando um shell para cada arquivo. Usar + para passar vários arquivos de uma vez para o comando sh e processá-los com um loop for fornece uma vantagem perceptível de desempenho:

find . -exec sh -c 'for f do file -b "$f" | grep -qE "^ELF|^C source" && printf %s\n "$f"; done' sh {} +

Você pode ver a comparação para si mesmo executando os dois comandos a seguir e comparando a saída de time :

time find . -exec sh -c 'for f do file -b "$f" | grep -qE "^ELF|^C source" && printf %s\n "$f"; done' sh {} +
time find . -exec sh -c 'file -b "$1" | grep -qE "^ELF|^C source" && printf %s\n "$1"' sh {} \;

O ponto real, no entanto, é:

Nunca execute um loop for do shell em uma lista de arquivos com saída de find . Em vez disso, execute a ação que você precisa fazer em cada arquivo diretamente em find de usando o operador -exec , ou embed um shell for loop dentro de um comando find e faça desta forma.

Algumas razões adicionais:

por 07.04.2016 / 01:06
3

É mais fácil executar um script pequeno para cada arquivo que verifica a saída de modo breve de file e imprime o caminho se a saída de file corresponder a ELF ou C source , o caminho é passado em como $0 .

find . -type f -exec sh -c \
    'file -b "$0" | grep -q "^ELF\|^C source" && printf %s\n "$0"' {} \;

Esta solução tem as seguintes vantagens em relação ao original

-type f filtra os diretórios imediatamente em vez de confiar na saída de file

Passando no argumento como {} evita problemas relacionados a espaços em branco ou novas linhas no nome do arquivo.

    
por 06.04.2016 / 21:56

Tags