bash - posso fazer: find… -exec this && that?

21

Existe uma maneira de combinar logicamente dois comandos shell que são chamados com find - exec ?

Por exemplo, para imprimir todos os arquivos .csv que contenham a string foo junto com sua ocorrência, gostaria de fazer:

find . -iname \*.csv -exec grep foo {} && echo {} \;

mas bash reclama com "argumento ausente para '-exec'"

    
por Marcus Junius Brutus 13.11.2012 / 19:49

3 respostas

23

-exec é um predicado que executa um comando (não um shell) e é avaliado como verdadeiro ou falso baseado no resultado do comando (zero ou não). status de saída zero).

Então:

find . -iname '*.csv' -exec grep foo {} \; -print

seria imprimir o caminho do arquivo se grep encontrar foo no arquivo. Em vez de -print , você pode usar outro predicado -exec ou qualquer outro predicado

find . -iname '*.csv' -exec grep foo {} \; -exec echo {} \;

Veja também os operadores ! e -o find para negação e ou .

Como alternativa, você pode iniciar um shell como:

find . -iname '*.csv' -exec sh -c '
   grep foo "$1" && echo "$1"' sh {} \;

Ou para evitar ter que iniciar um shell para cada arquivo:

find . -iname '*.csv' -exec sh -c '
  for i do
    grep foo "$i" && echo "$i"
  done' sh {} +
    
por 13.11.2012 / 22:50
9

O problema que você está enfrentando é que o shell primeiro analisa a linha de comando e vê dois comandos simples separados pelo operador && : find . -iname \*.csv -exec grep foo {} e echo {} \; . A citação de && ( find . -iname \*.csv -exec grep foo {} '&&' echo {} \; ) ignora isso, mas agora o comando executado por find é algo como grep com os argumentos foo , wibble.csv , && , echo e wibble.csv . Você precisa instruir find para executar um shell que interpretará o operador && :

find . -iname \*.csv -exec sh -c 'grep foo "$0" && echo "$0"' {} \;

Observe que o primeiro argumento após sh -c SOMECOMMAND é $0 , não $1 .

Você pode salvar o tempo de inicialização de um processo de shell para cada arquivo, agrupando as invocações de comando com -exec … + . Para facilitar o processamento, passe algum valor fictício como $0 para que "$@" enumere os nomes dos arquivos.

find . -iname \*.csv -exec sh -c 'for x in "$@"; do grep foo "$x" && echo "$x"; done' \ {} +

Se o comando shell for apenas dois programas separados por && , find poderá fazer o trabalho sozinho: escreva duas ações -exec consecutivas e a segunda só será executada se a primeira sair com a ação status 0.

find . -iname \*.csv -exec grep foo {} \; -exec echo {} \;

(Eu assumo que grep e echo são apenas para fins ilustrativos, pois -exec echo pode ser substituído por -print e a saída resultante não é particularmente útil de qualquer maneira.)

    
por 13.11.2012 / 23:09
3

Neste caso específico eu faria:

find . -iname \*.csv -exec grep -l foo \{\} \;

Ou se você tiver ack :

ack -al -G '.*\.csv' foo

Para responder à sua pergunta real, algo assim pode funcionar:

find . -iname \*.csv -exec sh -c "grep foo {} && echo {}" \;

    
por 13.11.2012 / 19:55

Tags