Convença o grep a exibir todas as linhas, não apenas as que correspondem

121

Digamos que tenho o seguinte arquivo:

$ cat test

test line 1
test line 2
line without the search word
another line without it
test line 3 with two test words
test line 4

Por padrão, grepretorna cada linha que contém o termo de pesquisa:

$ grep test test

test line 1
test line 2
test line 3 with two test words
test line 4

Passar o --colorparâmetro para grepfará com que ele destaque a parte da linha que corresponde à expressão de pesquisa, mas ainda retorna apenas as linhas que contêm a expressão. Existe uma maneira de obter greptodas as linhas no arquivo de origem, mas realce as correspondências?

Meu terrível truque atual para fazer isso (pelo menos em arquivos que não têm mais de 10000 linhas consecutivas sem correspondências) é:

$ grep -B 9999 -A 9999 test test

Captura de tela dos dois comandos

Se grepnão for possível, existe outra ferramenta de linha de comando que ofereça a mesma funcionalidade? Eu brinquei com isso ack, mas também não parece ter uma opção.

Michael Mrozek
fonte
1
Possível duplicata entre sites de: stackoverflow.com/questions/981601/… A resposta principal é a mesma em ambas.
Ciro Santilli escreveu:
1
Você pode usar -C 9999no lugar de -A 9999 -B 9999.Eu sempre faço:grep -C 9999 pattern file
neo

Respostas:

184
grep --color -E "test|$" yourfile

O que estamos fazendo aqui é comparar com o $padrão e o padrão de teste, obviamente $não tem nada para colorir, então apenas o padrão de teste fica colorido. O -Eapenas ativa a correspondência estendida de regex.

Você pode criar uma função facilmente dessa forma:

highlight () { grep --color -E "$1|$" "${@:1}" ; }
jacksonh
fonte
14
Isso é incrível!
gvkv
3
E como uma função: highlight () { grep --color -E "$1|$" $2 ; }. Uso:highlight test yourfile
Stefan Lasiewski
2
@StefanLasiewski: "$2"também deve ser citado.
Dennis Williamson
6
Melhor poderia ser: o highlight () { grep --color -E "$1|$" "$@" }que permite arquivos com espaço em branco em seus nomes e vários arquivos.
Mike DeSimone
15
@ MikeDeSimone - Mas isso também terá "$1"nos arquivos. Usehighlight () { grep --color -E "$1|$" "${@:1}" }
Chris Down
40
ack --passthru --color string file

para Ubuntu e Debian, use ack-grep em vez de ack

ack-grep --passthru --color string file
Dennis Williamson
fonte
1
Oh, isso é óbvio na página de manual; Eu estou cego. Obrigado
Michael Mrozek
o padrão para grap pode ser especificado explicitamente com --regexp string; o ack / ack-grep equivalente é--match string
Rhubbarb
Para simular ack --passthrucom perl:grep_passthrough () { ! perl -ne "print; \$e ||= /$@/; END{{exit \$e}}"; }
Lucas Cimon 4/16
ack --passthrutambém funciona em fluxos! Por exemplo,ifconfig | ack --passthru inet
honkaboy 16/06
13

Outra maneira de fazer isso corretamente e portably com grep(além de usar duas expressões regulares com alternância como na resposta aceita) é via o padrão nulo (e corda respectivamente null).
Ele deve funcionar igualmente bem com os dois -Ee com os -Fswitches, pois, de acordo com o padrão :

-E
    Match using extended regular expressions. 
    [...] A null ERE shall match every line.

e

-F
    Match using fixed strings.
    [...] A null string shall match every line.

Então é simplesmente uma questão de correr

grep -E -e '' -e 'pattern' infile

e respectivamente

grep -F -e '' -e 'string' infile
don_crissti
fonte
1
Ou apenas grep -F -e '' -e 'string'.
Stéphane Chazelas
Funciona para mim com o GNU grep 2.25, o busybox grep e o pacote de ferramentas da herança.
Stéphane Chazelas
OK, meu mal, seq 3 | grep -F -e '' -e 2gera apenas 2 em 2,27 (e 2,26, mas não 2,25, e não na cabeça do git). Somente com -F(não sem ou com -E). IOW, apenas 2.26 e 2.27 parecem ter o erro e apenas com -F.
Stéphane Chazelas
Note que seq 3 | grep -F -e '' -e '' -e 2 parece funcionar bem com 2.27
Stéphane Chazelas
1
O método dado nesta resposta é simples, correto e parece bastante rápido . Como você diz , ele funciona automaticamente -F, aliviando a necessidade de se preocupar com o que os personagens aparecem string. Você pode querer mencionar --color; À medida que essa resposta flutua na página (quando vista por votos), mais pessoas podem acabar lendo-a antes das outras respostas.
Eliah Kagan
11

Eu tenho a seguinte função que eu uso para essas coisas:

highlight () {
    perl -pe "s/$1/\e[1;31;43m$&\e[0m/g"
}

Internamente, parece meio feio, é agradável e fácil de usar, assim:

cat some_file.txt | highlight some_word

ou, para um exemplo um pouco mais real:

tail -f console.log | highlight ERROR

Você pode alterar as cores para o que quiser (o que pode ser difícil com o grep - não tenho certeza) alterando o 1e 31e 43(depois \e[) para valores diferentes. Os códigos a serem usados ​​estão por todo o lado , mas aqui está uma introdução rápida: o 1 coloca o texto em negrito, o 31torna vermelho e o 43amarelo fornece um fundo amarelo. 32ou 33seriam cores diferentes e 44ou 45seriam fundos diferentes: você entendeu a idéia. Você pode até fazê-lo piscar (com a 5) se quiser.

Isso não usa módulos Perl especiais, e o Perl é quase onipresente, portanto, espero que funcione em qualquer lugar. A solução grep é muito inteligente, mas a opção --color no grep não está disponível em todos os lugares. Por exemplo, eu apenas tentei esta solução em uma caixa Solaris executando o bash e outro executando o ksh, e minha máquina local do Mac OS X executando o zsh. Tudo funcionou muito bem. Solaris engasgou com a grep --colorsolução, no entanto.

Além disso, o ack é incrível, e eu o recomendo para quem ainda não o descobriu, mas tive alguns problemas ao instalá-lo em alguns dos muitos servidores em que trabalho. (Eu esqueço o porquê: acho que relacionado aos módulos Perl necessários).

Como acho que nunca trabalhei em uma caixa Unix que não tinha o Perl instalado (com exceção de sistemas do tipo incorporado, roteadores Linksys e outros), eu diria que essa é uma solução praticamente universal. .

iconoclasta
fonte
3

Em vez de usar o Grep, você pode usar Menos:

less file

Pesquisar assim: /pattern Enter

Isso vai:

  1. role até a primeira linha correspondente
  2. produzir todas as linhas a partir daí
  3. destacar todas as correspondências

Para rolar para a próxima linha correspondente: n

Para rolar para a linha correspondente anterior: N

Para alternar o destaque: Esc u

Além disso, você pode alterar a cor de destaque, se quiser.

Steven Penny
fonte
2

Podes tentar:

perl -MTERM::ANSIColor -nle '/pattern/ ? print colored($_, 'color') : print' test

No entanto, não é muito portátil e, mesmo que o Perl esteja instalado, você pode precisar fazer o download de outro módulo. Além disso, colorirá a linha inteira, não apenas a palavra de pesquisa.

gvkv
fonte
2

ripgrep

Use ripgrepcom seu --passthruparâmetro:

rg --passthru pattern file.txt

É uma das ferramentas de grepping mais rápidas , pois é construída sobre o mecanismo de expressão regular da Rust, que usa autômatos finitos, SIMD e otimizações literais agressivas para tornar a pesquisa muito rápida.

--passthru - Imprima linhas correspondentes e não correspondentes.

Outra maneira de obter um efeito semelhante é modificando seu padrão para corresponder à sequência vazia. Por exemplo, se você estiver pesquisando usando rg foo, o uso rg "^|foo"será emitido em todas as linhas de todos os arquivos pesquisados, mas apenas as ocorrências de foo serão destacadas. Esse sinalizador permite o mesmo comportamento sem precisar modificar o padrão.

kenorb
fonte
1

Existe uma maneira muito mais fácil de fazer isso no GNU grep, mas não acho que seja portátil (ou seja, BSD grep):

Em um tubo:

cat <file> | grep --color=always -z <query>

Em um arquivo:

grep --color=always -z <query> <file>

O crédito vai para a resposta de Cyrus aqui .

Erik Nomitch
fonte
1
Esta não é uma resposta tão boa quanto você (e Cyrus) pensam. Ele tem o efeito de tratar o arquivo inteiro como uma única linha. Portanto, (1) se o arquivo for muito grande, pode haver uma possibilidade de ficar sem memória, e (2) se o arquivo não contém o padrão de todo , então nada será emitido. E você está certo; não está universalmente disponível. (Mas, novamente, --colornão é normal também.)
G-Man
Em qual versão do grep isso é suportado? Em grep 2.5.4, -z não parece disponível ...
Alex
1

Nenhuma das respostas dadas até o momento fornece uma solução portátil .

Aqui é um portátil 1 função shell que eu já postado em um fechado como questão duplicado que não necessita de ferramentas padrão não ou extensões não padrão fornecidos com perl, ack, ggrep, gsed, bashe os gostos, mas só precisa de um shell POSIX e os utilitários obrigatórios POSIX sede printf:

grepc()
{
  pattern=$1
  shift
  esc=$(printf "\033")
  sed 's"'"$pattern"'"'$esc'[32m&'$esc'[0m"g' "$@"
}

Você pode usá-lo dessa maneira:

grepc string_to_search [file ...]

A cor do destaque pode ser ajustada usando um destes códigos no argumento sed command (32m sendo verde aqui):

30m black
31m red
33m yellow
34m blue
35m magenta
36m cyan
37m white
7m reverse video

1 Desde que o seu terminal suporte sequências de escape de cores ANSI.

jlliagre
fonte
0

Uma sedversão, funciona em ambos bashe ash.

#highlight
hsed(){
    local pattern="$1"
    shift
    local r=`echo -e '\e'[31m`
    local c=`echo -e '\e'[0m`
    sed "s:${pattern//:/\:}:$r\0$c:g" "$@"
}
yurenchen
fonte
0

O OP pediu grep, e é isso que RECOMENDO ; mas depois de tentar resolver um problema com sed, para constar, aqui está uma solução simples:

sed $'s/main/\E[31m&\E[0m/g' testt.c

ou

cat testt.c | sed $'s/main/\E[31m&\E[0m/g'

Pintará mainem vermelho.

  • \E[31m : sequência de início de cor vermelha
  • \E[0m : marca de cor finalizada
  • & : o padrão correspondente
  • /g : todas as palavras em uma linha, não apenas a primeira
  • $'string' é bash strings com caracteres de escape interpretados

Em relação ao grep , ele também funciona usando ^(início de linha) em vez de $(fim de linha). Exemplo:

egrep "^|main" testt.c

E só para mostrar esse pseudônimo louco que NÃO RECOMENDO , você pode até deixar as aspas abertas:

alias h='egrep -e"^|'
h main" testt.c
cat testt.c | h main"

todo o trabalho! :) Não se preocupe se você esquecer de fechar a cotação, o bash lembrará de você com um "caractere de linha contínua".

Dr Beco
fonte