find: multiple '-exec's com condições

1

Estou tentando incluir o pacote C padrão stdint.h em arquivos com a palavra LARGE_INTEGER , como parte da conversão do Windows para drivers Linux, conforme discutido aqui para tipos de dados. Eu conheço os tópicos anteriores sobre find e xargs, aqui e aqui .

O código em que a parte do GNU encontra-se baseia-se principalmente neste tópico :

gfind /tmp/ -type f                                                      \
    \( -name "*.h" -o -name "*.cpp" \)                                   \
    -exec ggrep -Pl "LARGE_INTEGER" {} +

e sua extensão de pseudocódigo onde eu quero também que os arquivos contenham a palavra LARGE_INTEGER

gfind /tmp/ -type f                                                \
    \( -name "*.h" -o -name "*.cpp" \)                             \
    -and -exec ggrep -Pl "LARGE_INTEGER" {} \;                     \
    | xargs gsed -i '1s/^/#include <stdint.h>\n/'

onde eu não tenho certeza sobre -and e dando

gsed: can't read /tmp/: No such file or directory
...

Eu segui exemplos em commandlinefu aqui .

Como você pode combinar um novo comando ao find baseado no GNU SED?

    
por Léo Léopold Hertz 준영 30.06.2015 / 00:01

2 respostas

3

Eu usaria 1 find com duas -exec actions, por exemplo:

find . -type f -exec grep -qF SOME_STRING {} \; -exec sed 'COMMAND' {} \;

O segundo comando será executado somente se o primeiro for avaliado como verdadeiro , ou seja, código de saída 0 , então sed processará o arquivo em questão somente se o arquivo contém SOME_STRING . É fácil ver como funciona:

find . -type f -exec grep -qF SOME_STRING {} \; -print

ele deve listar apenas os arquivos que contêm SOME_STRING . Claro, você sempre pode encadear mais de duas expressões e também usar operadores como ! (negação), por exemplo:

find . -type f -exec grep -qF THIS {} \; ! -exec grep -qF THAT {} \; -print

listará apenas os arquivos que contêm THIS , mas não contêm THAT .
De qualquer forma, no seu caso:

gfind /tmp/ -type f \( -name "*.h" -o -name "*.cpp" \) \
-exec ggrep -qF LARGE_INTEGER {} \; \
-exec gsed -i '1s/^/#include <stdint.h>\n/' {} \;

1
Eu suponho que sua xargs não suporta a opção -0 ou --null . Em caso afirmativo, use a seguinte construção:

find . -type f -exec grep -lFZ SOME_STRING {} + | xargs -0 gsed -s -i 'COMMAND'

i.e. no seu caso:

gfind /tmp/ -type f \( -name "*.h" -o -name "*.cpp" \) \
-exec ggrep -lFZ LARGE_INTEGER {} + | \
xargs -0 gsed -s -i '1s/^/#include <stdint.h>\n/'

Deve ser mais eficiente que o primeiro.
Além disso, ambos funcionarão com todos os tipos de nomes de arquivos. Note que estou usando grep com -F (string fixa), pois é mais rápido, então remova-o se você estiver planejando usar um regex.

    
por 30.06.2015 / 01:00
1

Basta enviar a saída de gfind para xargs :

gfind /tmp/ -type f \( -name "*.h" -o -name "*.cpp" \) -exec ggrep -l "LARGE_INTEGER" {} + | xargs sed -i '1s/^/#include <stdint.h>\n/'

Observe que removi a opção -P de ggrep , pois você está combinando com uma string fixa.

No entanto, esta solução não lida bem com nomes de arquivos contendo novas linhas; uma maneira mais segura de fazer isso seria forçar gfind a gerar nomes de arquivos terminados em NULL e fazer um loop sobre a saída em um loop while :

#!/bin/bash

gfind /tmp/ -type f \( -name "*.h" -o -name "*.cpp" \) -print0 | while read -d '' -r filepath; do
    [ "$(ggrep -l "LARGE_INTEGER" "$filepath")" ] && sed -i '1s/^/#include <stdint.h>\n/' "$filepath"
done

Se você gosta de one-liners:

gfind /tmp/ -type f \( -name "*.h" -o -name "*.cpp" \) -print0 | while read -d '' -r filepath; do [ "$(ggrep -l "LARGE_INTEGER" "$filepath")" ] && sed -i '1s/^/#include <stdint.h>\n/' "$filepath"; done
    
por 30.06.2015 / 00:21

Tags