Linux / Comando grep: buscar texto em arquivos e filtrar saídas

grep é um comando usado para encontrar linhas que correspondam a um padrão especificado em arquivos ou na saída de comandos. Ele oferece suporte não apenas a pesquisas básicas, mas também a ignorar maiúsculas e minúsculas, selecionar linhas que não correspondem, buscas recursivas em diretórios, expressões regulares e exibição do contexto ao redor.

A forma mais básica é grep 'pattern' filename. Como o padrão pode conter caracteres que têm significados especiais no shell, convém colocar o padrão entre aspas simples.

O que é o comando grep?

grep lê a entrada linha por linha e exibe as linhas que correspondem ao padrão. O nome vem da expressão de comando no editor de texto Unix ed, g/re/p, que significa encontrar e imprimir linhas que correspondem a uma expressão regular em toda a extensão.

Sem um arquivo informado e sem uma opção de busca recursiva, o grep lê a entrada padrão. Assim, o operador de canalização (pipe) | permite pesquisar a saída de outro comando. Por padrão, o GNU grep usa expressões regulares básicas (BRE, de Basic Regular Expression); -E seleciona expressões regulares estendidas (ERE, de Extended Regular Expression). Uma expressão regular descreve um padrão de texto; ela pode conter operadores, além de caracteres literais.

Sintaxe básica e primeira busca

A sintaxe básica é a seguinte. Colchetes indicam itens opcionais, portanto, não os insira no comando real.

grep [option] 'pattern' [file...]

Os arquivos e diretórios dos exemplos representam entradas que precisam existir e permitir leitura. Adapte os caminhos ao seu sistema. Para reproduzir a busca e as contagens deste artigo, crie um arquivo de texto chamado app.log com estas quatro linhas, na ordem indicada:

  • INFO start
  • ERROR disk
  • WARN retry
  • ERROR timeout ERROR

Para encontrar as linhas que contêm ERROR, execute:

grep 'ERROR' app.log

No arquivo de exemplo, a saída contém ERROR disk e ERROR timeout ERROR. Com -n, os números dessas linhas são 2 e 4.

Se você especificar vários arquivos, o nome do arquivo aparece junto com cada resultado.

grep 'ERROR' app.log worker.log

Você também pode pesquisar a entrada padrão. O seguinte comando mostra as linhas nas mensagens do kernel que contêm usb, sem diferenciar maiúsculas de minúsculas.

dmesg | grep -i 'usb'

Em alguns sistemas, a política dmesg_restrict limita a leitura das mensagens do kernel, conforme o manual de dmesg. Um erro de permissão nessa etapa não significa que o grep falhou. Se você tiver autorização administrativa, pode executar sudo dmesg | grep -i 'usb'.

Opções de grep frequentemente usadas

Opção Função Exemplos de uso
-i Pesquisar sem diferenciar maiúsculas de minúsculas grep -i 'error' app.log
-v Selecione linhas que não correspondem grep -v '^#' config
-n Mostre números de linha no resultado grep -n 'TODO' main.c
-w Pesquise o padrão como uma palavra inteira grep -w 'cat' words.txt
-x Selecione a linha inteira quando corresponder ao padrão grep -x 'enabled' status.txt
-F Trate o padrão como uma string fixa, não como uma expressão regular grep -F 'a.b' data.txt
-E Use expressões regulares estendidas grep -E 'error|failed' app.log
-r Pesquise recursivamente em subdiretórios grep -r 'listen' /etc/nginx
-l Imprima apenas nomes de arquivos correspondentes grep -rl 'deprecated' src
-c Imprima a contagem de linhas correspondentes por arquivo grep -c 'ERROR' app.log
-A, -B, -C Imprima o contexto depois, antes ou ao redor das linhas correspondentes grep -C 2 'panic' app.log

As opções acima podem ser combinadas. Por exemplo, grep -in 'error' app.log irá buscar ignorando maiúsculas e minúsculas e também exibirá números de linha. O comportamento detalhado e as opções de extensão GNU podem ser encontradas na documentação oficial do GNU Grep.

Como distinguir strings de expressões regulares

Pesquisar strings fixas com -F

Uma string é uma sequência de caracteres. Caracteres que têm significados especiais em expressões regulares, como pontos, asteriscos e colchetes, podem ser pesquisados literalmente usando -F. No comando a seguir, o ponto é tratado como um ponto real, e não como qualquer caractere.

grep -F '192.168.0.1' access.log

Se o termo de pesquisa for uma string simples, você pode reduzir caracteres de escape desnecessários e tornar sua intenção clara.

Use expressões regulares estendidas com -E

Usar -E permite que você utilize a sintaxe de expressões regulares estendidas, como |, +, ?, e parênteses, de forma mais legível. O exemplo a seguir encontra linhas que contêm um de ERROR, WARN ou FATAL.

grep -E 'ERROR|WARN|FATAL' app.log

No GNU grep, a diferença entre expressões regulares básicas e estendidas reside principalmente na notação. Se a compatibilidade com outros ambientes for importante, verifique a implementação de grep e as opções suportadas no sistema alvo.

Especifique o início e o fim de uma linha

O acento circunflexo (^) representa o início de uma linha, e o sinal de dólar ($) representa o fim de uma linha. Uma linha vazia pode ser encontrada com ^$, que não possui caracteres entre o início e o fim.

grep '^server' config.txt
grep 'completed$' job.log
grep '^$' notes.txt

Para excluir linhas vazias e linhas de comentários de um arquivo de configuração, você pode aplicar as duas condições em sequência da seguinte forma.

grep -v '^[[:space:]]*#' app.conf | grep -v '^[[:space:]]*$'

Restringindo o intervalo de pesquisa com precisão

Correspondendo palavras inteiras e linhas inteiras

-w seleciona linhas onde o padrão corresponde à palavra inteira. Por exemplo, grep -w 'cat' encontrará cat mas não selecionará cat dentro de category. Nesse teste, letras, dígitos e o sublinhado _ são caracteres de palavra. Por isso, cat_1 não corresponde a -w cat, enquanto cat-1 corresponde; não se trata de uma análise linguística de palavras.

grep -w 'root' /etc/passwd

-x seleciona uma linha apenas quando a linha inteira corresponde exatamente ao padrão.

grep -x 'active' service-status.txt

Encontrando linhas que não correspondem

-v inverte a seleção. O exemplo a seguir imprime apenas linhas que não contêm DEBUG.

grep -v 'DEBUG' app.log

Quando combinado com -i, você pode excluir sem se importar com maiúsculas ou minúsculas.

grep -iv 'debug' app.log

Pesquisando vários padrões de uma vez

Usar -e várias vezes permite que você especifique cada padrão separadamente. Você não precisa combiná-los em uma única expressão regular complexa, tornando mais fácil de ler em scripts.

grep -e 'ERROR' -e 'FATAL' app.log

Se houver muitos padrões, você pode criar um arquivo com um padrão por linha e lê-lo com -f. Por exemplo, coloque ERROR e FATAL em linhas separadas de patterns.txt.

grep -f patterns.txt app.log

Como pesquisar diretórios recursivamente

Diferença entre -r e -R

-r pesquisa recursivamente arquivos no diretório especificado. Ele segue links simbólicos especificados diretamente na linha de comando, mas ignora links simbólicos encontrados durante pesquisas recursivas.

grep -rn 'database_url' ./config

No GNU grep, -R também segue links simbólicos encontrados durante a busca. Isso pode levar a ciclos já existentes ou ampliar a busca para fora da árvore esperada; o comando não cria links. Use-o quando esse comportamento for necessário.

Como incluir e excluir tipos de arquivo

--include pesquisa apenas pelos padrões de nomes de arquivo especificados, e --exclude pula arquivos que correspondem. Coloque os padrões entre aspas para que o shell não expanda os asteriscos primeiro.

grep -rn --include='*.conf' 'timeout' /etc
grep -rn --exclude='*.min.js' 'deprecated' ./src

Para excluir diretórios específicos, use --exclude-dir.

grep -rn --exclude-dir='.git' --exclude-dir='node_modules' 'api_key' .

Como listar somente os nomes dos arquivos

Se você precisar de uma lista de arquivos em vez de conteúdo correspondente, use -l. Cada arquivo deixa de ser pesquisado após a primeira correspondência encontrada, o que também atende bem ao propósito.

grep -rl --include='*.html' 'jb-heading' .

Por outro lado, para encontrar apenas arquivos sem padrões correspondentes, use -L em maiúsculas.

grep -rL --include='*.html' 'jb-heading' .

Como apresentar os resultados de forma legível

Números de linha e nomes de arquivo

-n mostra os números das linhas correspondentes. Para exibir o nome do arquivo mesmo ao pesquisar um único arquivo, use -H do GNU grep; para ocultar o nome, use -h.

grep -nH 'listen' nginx.conf

Visualizando o contexto antes e depois das linhas correspondentes

Se for difícil determinar a causa apenas pelas linhas de erro, use -A, -B ou -C para visualizar as linhas ao redor. O número indica quantas linhas adicionais são impressas.

grep -A 3 'ERROR' app.log
grep -B 2 'ERROR' app.log
grep -C 2 'ERROR' app.log

-A 3 adiciona 3 linhas depois, -B 2 adiciona 2 linhas antes e -C 2 adiciona 2 linhas antes e depois.

Imprimindo apenas a parte correspondente

-o imprime as partes não vazias que correspondem ao padrão, em vez da linha inteira. As correspondências são encontradas sem sobreposição e aparecem em linhas separadas.

grep -oE '[0-9]{3}-[0-9]{4}' contacts.txt

Para destacar correspondências no terminal, você pode usar --color=auto. Ao enviar a saída para um arquivo ou outro comando, use auto ou never para evitar que os caracteres de controle de cor sejam incluídos.

Usando-o em logs e saídas de comando

Como encontrar erros em logs recentes

Para pesquisar apenas o intervalo recente em vez do log inteiro, conecte-o com tail.

tail -n 500 app.log | grep -iE 'error|failed|fatal'

Se você quiser filtrar continuamente os novos logs adicionados ao arquivo, você pode usá-lo da seguinte forma.

tail -F app.log | grep --line-buffered 'ERROR'

--line-buffered é uma opção de extensão GNU que descarrega o buffer após cada linha de saída, reduzindo atrasos no pipe. Ela não elimina o armazenamento em buffer de programas anteriores na cadeia. Para interromper esse acompanhamento em primeiro plano, pressione Ctrl+C; isso encerra o pipeline de leitura, sem parar o programa que escreve o log. Consulte os detalhes sobre acompanhamento e rotação no guia do comando tail no Linux.

Como filtrar serviços e processos

Em sistemas Linux que usam systemd, você pode procurar um nome de serviço na saída do systemctl.

systemctl list-unit-files | grep 'nginx'

Se o seu objetivo é apenas verificar a existência de um processo, pgrep frequentemente é mais direto do que ps | grep. Para buscar pelo nome do comando e seus argumentos juntos, você pode executá-lo da seguinte forma.

pgrep -af 'nginx'

Como contar linhas correspondentes

-c imprime o número de linhas correspondentes, não o número de correspondências. Mesmo que uma linha contenha o padrão várias vezes, essa linha é contada apenas uma vez.

grep -c 'ERROR' app.log

No arquivo de quatro linhas preparado no início, o resultado é 2, pois duas linhas contêm ERROR.

Para contar as ocorrências não sobrepostas de ERROR, separe cada correspondência com -o e conte as linhas da saída com wc -l. Nesse mesmo arquivo, o resultado será 3, pois a última linha contém duas ocorrências.

grep -o 'ERROR' app.log | wc -l

Verificar existência sem saída

-q não imprime resultados e apenas retorna um código de saída. É adequado para verificar a existência de um padrão em uma condicional de shell.

if grep -q 'ready' status.log; then
 echo 'Service ready'
fi

Entendendo o código de saída do grep

grep comunica os resultados da busca não apenas através da string de saída, mas também pelo código de saída. Em uma busca normal de linhas correspondentes, retorna 0 se houver linhas correspondentes, 1 se não houver linhas correspondentes, e 2 se não puder ler um arquivo ou houver um problema de sintaxe.

Código de saída Significado Decisão em scripts
0 Uma ou mais correspondências encontradas Processar como se a condição fosse verdadeira
1 Nenhuma correspondência encontrada Pode ser um resultado de busca normal
2 Ocorreu um erro Verifique o arquivo de entrada, permissões ou padrão

Portanto, você não deve concluir que a execução do comando falhou apenas porque não há resultados. Na automação, é necessário distinguir entre 1 e um erro real 2. GNU grep -q pode terminar com o status 0 se uma correspondência for encontrada, mesmo que existam outros erros, portanto, se detectar erros em todos os arquivos de entrada for importante, uma verificação separada é necessária.

Problemas e soluções comuns

Quando o padrão começa com um hífen

Padrões como -ERROR podem ser confundidos com opções. Coloque o padrão após --, que indica o fim das opções.

grep -- '-ERROR' app.log

Quando aparece a mensagem Binary file matches

Se o GNU grep determinar que a entrada é um arquivo binário, ele pode exibir a mensagem em vez das linhas correspondentes. Se você tiver certeza de que é realmente um arquivo de texto, pode tratá-lo como texto usando -a.

grep -a 'ERROR' mixed-data.log

Por outro lado, para excluir arquivos binários em pesquisas recursivas, você pode usar -I. Não use sempre -a em dados binários desconhecidos, pois podem conter caracteres de controle que podem bagunçar seu terminal.

Quando os resultados diferem devido a caracteres especiais de regex

Na busca usual por linhas, o ponto corresponde a um caractere e o asterisco permite zero ou mais repetições da expressão anterior. Se você pretendia buscar uma string simples, use -F, e se você pretendia usar uma expressão regular, coloque todo o padrão entre aspas simples para evitar a expansão do shell.

Quando buscas recursivas são muito lentas ou produzem muitos resultados

Reduza o diretório inicial para a busca e exclua arquivos desnecessários usando --include, --exclude e --exclude-dir. Para ocultar apenas erros de permissão, você pode se sentir tentado a descartar todos os erros padrão, mas fazê-lo pode fazer você perder problemas importantes. Portanto, é melhor primeiro verificar o escopo da busca e as permissões dos arquivos.

Perguntas frequentes

O grep distingue entre letras maiúsculas e minúsculas por padrão?

Sim. A busca padrão diferencia maiúsculas de minúsculas. Para ignorar a diferença entre maiúsculas e minúsculas, use -i. Você também deve considerar que os resultados do tratamento de maiúsculas e minúsculas podem variar dependendo da configuração regional (locale).

Qual é a diferença entre grep -r e grep -R?

No GNU grep, ambos realizam buscas recursivas, mas -R segue todos os links simbólicos encontrados durante a busca. Para buscas típicas de código-fonte ou arquivos de configuração, -r é mais fácil de prever.

É um erro se nenhum resultado de busca for encontrado?

Não necessariamente. Se não houver correspondências, geralmente ele retorna um código de saída de 1, enquanto erros como falha de acesso ao arquivo ou opções inválidas são diferenciados pelo status 2.

Como encontro linhas que contêm pelo menos um de vários termos de pesquisa?

grep -E 'error|warning' filename usa o operador de alternância em expressões regulares estendidas, ou você pode repetir -e como grep -e 'error' -e 'warning' filename. Para tratar o termo de pesquisa como uma string simples em vez de uma expressão regular, você também pode usar -F.

Resumo

Para pesquisas gerais de strings, é bom se familiarizar com grep -F; para múltiplas condições de expressões regulares, grep -E; e para pesquisar em um diretório inteiro, grep -r. Para facilitar a interpretação dos resultados, você pode usar -n e -C; se você só precisar da lista de arquivos, use -l; e para verificar a existência em scripts, use -q.

Coloque os padrões entre aspas simples, limite o escopo das buscas recursivas e distinga entre o código de saída 1 e o código de erro 2. Para conhecer outras ferramentas básicas, consulte a lista de comandos frequentes do Linux (em inglês).