
Quero usar grep em arquivos que incluam o padrão A (iwant), mas quero excluir arquivos que contenham o padrão B (idontwant).
Exemplo:
read -p "...what are you looking for: " iwant
read -p "...what should not be included: " idontwant
iwant="blue car"
idontwant="red car"
Suponha que eu tenha os seguintes arquivos:
-rw-rw-r--. 1 terpentin terpentin 45 Jun 8 16:04 blue.car
-rw-rw-r--. 1 terpentin terpentin 44 Jun 8 16:05 mixed.car
-rw-rw-r--. 1 terpentin terpentin 40 Jun 8 16:04 red.car
find . -type f -print -exec cat {} \;
./mixed.car
blue car
red car
blue car
./red.car
red car
red car
red car
./blue.car
blue car
blue car
blue car
Como é possível obter apenas o arquivo "./blue.car" como resultado?
O conteúdo original inclui centenas de arquivos de texto longos, o que torna importante ser o mais eficiente possível em termos de recursos.
Responder1
Usar
find . -type f ! -exec grep -q "$idontwant" {} ';' -exec grep -q "$iwant" {} ';' -print
ou
find . -type f -exec grep -q "$iwant" {} ';' ! -exec grep -q "$idontwant" {} ';' -print
- Os termos (às vezes chamados de “predicados”) em um
find
comando são caracterizados comotestes(por exemplo,-type f
) eações(por exemplo,-print
e-delete
). Pode ser difícil descobrir na página de manual que-exec
é ao mesmo tempo um Açãoe um teste. Então, assim comoencontrar . -type f -mtime -30 -name '*.txt' -readable -size +5teste 6 teste 7 teste 8…
restringe sucessivamente a pesquisa a arquivos que atendam a todos os critérios (satisfaçam todos os testes especificados), entãoencontrar . -execcmd1{} ';' -execcmd2{} ';' -execcmd3{} ';' …
encontra arquivos para os quais todos os comandos foram bem-sucedidos. - Qualquer
find
teste pode ser negado (invertido) precedendo-o com!
. Assim,find . ! -type d
encontra arquivos simples, links simbólicos, pipes nomeados, soquetes e arquivos de dispositivos — tudo, exceto diretórios. - Observe que isso
! -exec grep …
não é equivalente a-exec grep -v …
.-exec grep -v …
encontrará arquivos que possuem pelo menos uma linha que não corresponde.! -exec grep …
encontrará arquivos ondenãoas linhas coincidem. - A
-q
opção togrep
é oficialmente sinônimo de--quiet
, mas também significarápido. Ele não grava nenhuma saída (exceto talvez mensagens de erro, conforme aplicável), mas também sai assim que encontra uma correspondência - ele não lê todos os arquivos até o fim para encontrartodocorresponder. (É claro que, se um arquivo não contiver nenhuma correspondência, serágrep
necessário lê-lo na íntegra para determinar isso.) - Então (DR) os comandos encontram os arquivos para os quais
grep -q "$eu quero" arquivo
consegue egrep -q "$idontwant"arquivo
falha (porque o precedemos com!
). - Os dois comandos são funcionalmente equivalentes, mas podem ter desempenho diferente (ou seja, podem levar tempos diferentes para serem executados). Se apenas alguns arquivos contiverem as strings de pesquisa,
encontrar . -type f -exec grep -q "$iwant" {} ';' ! -exec grep -q "$idontwant" {} ';' -imprimir
será mais rápido, poisgrep "$iwant"
eliminará a maioria dos arquivos. Se muitos dos arquivos contiverem ambas as strings, entãoencontrar . -digite f! -exec grep -q "$idontwant" {} ';' -exec grep -q "$eu quero" {} ';' -imprimir
será mais rápido, pois! grep "$idontwant"
eliminará a maioria dos arquivos.
Responder2
Com GNU grep
podemos realizar a extração do nome do arquivo com uma escolha criteriosa de opções regex e grep:
$ grep -lzPsr '(?s:(?=.*blue)(?!.*red))' .
Estamos operando o grep no modo slurp (-z), onde todo o arquivo é tratado como uma grande linha.
O -l listará os nomes dos arquivos que correspondem ao regex.
O -r será executado recursivamente em todos os arquivos no diretório atual e abaixo.
O -s silenciará o grep para não emitir nenhum aviso.
A regex procurará a presença de azul e ausência de vermelho em um arquivo para dizer sim.
O -P invoca o mecanismo regex Perl no grep para que possamos obter a vantagem dos regexes pcre.