Convencer a grep para generar todas las líneas, no solo aquellas con coincidencias

121

Digamos que tengo el siguiente archivo:

$ 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 defecto, grepdevuelve cada línea que contiene el término de búsqueda:

$ grep test test

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

Pasar el --colorparámetro a greplo hará resaltar la parte de la línea que coincide con la expresión de búsqueda, pero aún así solo devuelve líneas que contienen la expresión. ¿Hay alguna manera de obtener la grepsalida de cada línea en el archivo fuente, pero resaltar las coincidencias?

Mi truco terrible actual para lograr esto (al menos en archivos que no tienen más de 10000 líneas consecutivas sin coincidencias) es:

$ grep -B 9999 -A 9999 test test

Captura de pantalla de los dos comandos.

Si grepno puede lograr esto, ¿hay otra herramienta de línea de comandos que ofrezca la misma funcionalidad? He jugado con él ack, pero tampoco parece tener una opción para ello.

Michael Mrozek
fuente
1
Posible duplicado entre sitios de: stackoverflow.com/questions/981601/… La respuesta principal es la misma en ambos.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
Puede usar -C 9999en lugar de -A 9999 -B 9999.Siempre hago:grep -C 9999 pattern file
neo

Respuestas:

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

Lo que estamos haciendo aquí es hacer coincidir el $patrón y el patrón de prueba, obviamente $no tiene nada que colorear, por lo que solo el patrón de prueba adquiere color. El -Esolo activa la coincidencia de expresiones regulares extendida.

Puede crear una función fácilmente de esta manera:

highlight () { grep --color -E "$1|$" "${@:1}" ; }
jacksonh
fuente
14
¡Eso es increíble!
gvkv
3
Y en función: highlight () { grep --color -E "$1|$" $2 ; }. Uso:highlight test yourfile
Stefan Lasiewski
2
@StefanLasiewski: "$2"también debería ser citado.
Dennis Williamson
66
Mejor podría ser: highlight () { grep --color -E "$1|$" "$@" }que permite archivos con espacios en blanco en sus nombres y múltiples archivos.
Mike DeSimone
15
@MikeDeSimone - Pero eso también estará "$1"en los archivos. Usohighlight () { grep --color -E "$1|$" "${@:1}" }
Chris Down
40
ack --passthru --color string file

para Ubuntu y Debian, use ack-grep en lugar de ack

ack-grep --passthru --color string file
Dennis Williamson
fuente
1
Oh, eso es bastante obvio en la página del manual; Estoy ciego. Gracias
Michael Mrozek
el patrón para grap puede especificarse explícitamente con --regexp string; el equivalente ack / ack-grep es--match string
Rhubbarb
Para simular ack --passthrucon perl:grep_passthrough () { ! perl -ne "print; \$e ||= /$@/; END{{exit \$e}}"; }
Lucas Cimon
ack --passthru¡También funciona en transmisiones! Por ejemplo,ifconfig | ack --passthru inet
honkaboy
13

Otra forma de hacer esto correctamente y de forma portátil con grep(además de usar dos expresiones regulares con la alternancia como en la respuesta aceptada) es a través del modelo nulo (y la cadena, respectivamente nulo).
Debería funcionar igualmente bien con ambos -Ey con los -Finterruptores ya que, según el estándar :

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

y

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

Entonces es simplemente una cuestión de correr

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

y respectivamente

grep -F -e '' -e 'string' infile
don_crissti
fuente
1
O simplemente grep -F -e '' -e 'string'.
Stéphane Chazelas
A mí me funciona con GNU grep 2.25, busybox grep y heirloom toolchest one.
Stéphane Chazelas
OK, mi problema, seq 3 | grep -F -e '' -e 2solo produce 2 en 2.27 (y 2.26, pero no 2.25, y no en la cabeza de git). Solo con -F(no sin o con -E). IOW, solo 2.26 y 2.27 parecen tener el error y solo con -F.
Stéphane Chazelas
Tenga en cuenta que seq 3 | grep -F -e '' -e '' -e 2 parece funcionar también con 2.27
Stéphane Chazelas
1
El método dado en esta respuesta es simple, correcto y parece bastante rápido . Como dices , funciona automáticamente con -F, aliviando la necesidad de preocuparse por los personajes que aparecen string. Es posible que desee mencionar --color; A medida que esta respuesta aparece en la página (cuando se ve por votos), más personas pueden terminar leyéndola antes que las otras respuestas.
Eliah Kagan
11

Tengo la siguiente función que uso para tales cosas:

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

Internamente se ve un poco feo, es agradable y fácil de usar, así:

cat some_file.txt | highlight some_word

o, para un ejemplo un poco más real:

tail -f console.log | highlight ERROR

Puede cambiar los colores a lo que quiera (lo que podría ser difícil con grep, no estoy seguro) cambiando los 1y 31y 43(después \e[) a diferentes valores. Los códigos a utilizar son todo sobre el lugar , pero aquí es una introducción rápida: 1 negritas el texto, la 31hace que sea rojo, y la 43da un fondo amarillo. 32o 33serían diferentes colores, y 44o 45serían diferentes orígenes: se entiende la idea. Incluso puede hacer que parpadee (con un 5) si está tan inclinado.

Esto no utiliza ningún módulo especial de Perl, y Perl es casi omnipresente, por lo que esperaría que funcione en casi cualquier lugar. La solución grep es muy inteligente, pero el interruptor --color en grep no está disponible en todas partes. Por ejemplo, acabo de probar esta solución en una caja de Solaris que ejecuta bash, y otra que ejecuta ksh, y mi máquina Mac OS X local ejecuta zsh. Todo funcionó bien. grep --colorSin embargo, Solaris se atragantó con la solución.

Además, ack es increíble, y lo recomiendo a cualquiera que aún no lo haya descubierto, pero he tenido algunos problemas para instalarlo en algunos de los muchos servidores en los que trabajo. (Olvidé por qué: creo que relacionado con los módulos de Perl requería).

Como creo que nunca he trabajado en una caja de Unix que no tenía instalado Perl (con la excepción de los sistemas de tipo embebido, enrutadores Linksys, etc.), diría que esta es una solución prácticamente universal. .

iconoclasta
fuente
3

En lugar de usar Grep, puede usar Less:

less file

Buscar así: /pattern Enter

Esta voluntad:

  1. desplazarse a la primera línea coincidente
  2. salida de cada línea a partir de ahí en adelante
  3. resaltar todos los partidos

Para desplazarse a la siguiente línea coincidente: n

Para desplazarse a la línea coincidente anterior: N

Para alternar el resaltado: Esc u

También puede cambiar el color de resaltado si lo desea.

Steven Penny
fuente
2

Puedes probar:

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

Sin embargo, no es muy portátil, e incluso si Perl está instalado, es posible que deba descargar otro módulo. Además, coloreará toda la línea, no solo la palabra de búsqueda.

gvkv
fuente
2

ripgrep

Usar ripgrepcon su --passthruparámetro:

rg --passthru pattern file.txt

Es una de las herramientas de grepping más rápidas , ya que está construida sobre el motor regex de Rust que utiliza autómatas finitos, SIMD y optimizaciones literales agresivas para que la búsqueda sea muy rápida.

--passthru - Imprimir líneas coincidentes y no coincidentes.

Otra forma de lograr un efecto similar es modificando su patrón para que coincida con la cadena vacía. Por ejemplo, si está buscando usando, rg fooentonces usar en su rg "^|foo"lugar emitirá cada línea en cada archivo buscado, pero solo se resaltarán las ocurrencias de foo. Este indicador habilita el mismo comportamiento sin necesidad de modificar el patrón.

kenorb
fuente
1

Hay una manera mucho más fácil de hacer esto para GNU grep, pero no creo que sea portátil (es decir, BSD grep):

En una pipa:

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

En un archivo:

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

El crédito va a la respuesta de Cyrus aquí .

Erik Nomitch
fuente
1
Esta no es una respuesta tan buena como usted (y Cyrus) piensan. Tiene el efecto de tratar todo el archivo como una sola línea. Por lo tanto, (1) si el archivo es muy grande, puede haber una posibilidad de quedarse sin memoria, y (2) si el archivo no contiene el patrón en absoluto , entonces nada habrá salida. Y tienes razón; No está disponible universalmente. (Pero, de nuevo, --colortampoco es estándar .)
G-Man
¿En qué versión de grep es compatible? En grep 2.5.4, -z no parece estar disponible ...
Alex
1

Ninguna de las respuestas dadas hasta ahora proporciona una solución portátil .

Aquí es un portátil de 1 función de shell que ya se envió en un lugar cerrado como cuestión duplicado que no requiere herramientas no estándar o extensiones no estándar provistos de perl, ack, ggrep, gsed, bashy los gustos, pero sólo necesita un shell POSIX y los servicios públicos obligatorios POSIX sedy printf:

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

Puedes usarlo de esa manera:

grepc string_to_search [file ...]

El color de resaltado se puede ajustar mediante el uso de uno de estos códigos en el argumento del comando sed (32m es verde aquí):

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

1 Siempre que su terminal admita secuencias de escape de colores ANSI.

jlliagre
fuente
0

Una sedversión, funciona en ambos bashy 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
fuente
0

OP solicitó grep, y eso es lo que RECOMIENDO ; pero después de intentar resolver un problema con sed, para el registro, aquí hay una solución simple:

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

o

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

Pintará mainen rojo.

  • \E[31m : secuencia de inicio de color rojo
  • \E[0m : marca de color terminada
  • & : el patrón combinado
  • /g : todas las palabras en una línea, no solo la primera
  • $'string' Se interpretan cadenas bash con caracteres escapados

En cuanto a grep , también funciona usando ^(inicio de línea) en lugar de $(final de línea). Ejemplo:

egrep "^|main" testt.c

Y solo para mostrar este alias loco que NO RECOMIENDO , incluso puede dejar las citas abiertas:

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

¡todo el trabajo! :) No se preocupe si olvida cerrar la cita, bash lo recordará con un "carácter de línea continua".

Dr. Beco
fuente