Grep personajes antes y después del partido?

144

Usando esto:

grep -A1 -B1 "test_pattern" file

producirá una línea antes y después del patrón coincidente en el archivo. ¿Hay alguna forma de mostrar no líneas sino un número específico de caracteres?

Las líneas en mi archivo son bastante grandes, así que no estoy interesado en imprimir la línea completa, sino que solo observo la coincidencia en contexto. ¿Alguna sugerencia sobre cómo hacer esto?

Leyenda
fuente
1
Duplicado de unix.stackexchange.com/q/163726 Casi duplicado de stackoverflow.com/q/2034799
sondra.kinsey

Respuestas:

184

3 caracteres antes y 4 caracteres después

$> echo "some123_string_and_another" | grep -o -P '.{0,3}string.{0,4}'
23_string_and
ДМИТРИЙ МАЛИКОВ
fuente
55
Es una buena respuesta para pequeñas cantidades de datos, pero comienza a ralentizarse cuando se coinciden> 100 caracteres; por ejemplo, en mi archivo xml gigante, quiero {1,200} antes y después, y es demasiado lento para usar.
Benubird
3
La versión awk de @amit_g es mucho más rápida.
ssobczak
66
No está disponible en Mac OSX, por lo que realmente no es una solución ampliamente disponible. La versión -E (enumerada a continuación) es una mejor solución. ¿Qué es -P? Sigue leyendo ... -P, --perl-regexp Interpreta PATTERN como una expresión regular de Perl (PCRE, ver más abajo). Esto es altamente experimental y grep -P puede advertir sobre características no implementadas.
Xofo
2
En OSX, instale a través de: brew install homebrew/dupes/grepy ejecútelo como ggrep.
kenorb
1
Según lo implicado por @Benubird, esto será imposible de usar en términos de rendimiento para archivos enormes con entornos moderadamente amplios deseados para el objetivo del partido.
matanster
113
grep -E -o ".{0,5}test_pattern.{0,5}" test.txt 

Esto coincidirá con hasta 5 caracteres antes y después de su patrón. El modificador -o le dice a grep que solo muestre la coincidencia y -E que use una expresión regular extendida. Asegúrese de poner las comillas alrededor de su expresión, de lo contrario, podría ser interpretada por el shell.

ekse
fuente
1
Buena respuesta, interesante que tiene un límite de 2 ^ 8-1 para la longitud en {}, así que {0,255}funciona {0,256}dagrep: invalid repetition count(s)
CodeMonkey
Esto parece tener un rendimiento considerablemente menor a medida que aumento el número de caracteres coincidentes (5 -> 25 -> 50), ¿alguna idea de por qué?
Adam Hughes
37

Podrías usar

awk '/test_pattern/ {
    match($0, /test_pattern/); print substr($0, RSTART - 10, RLENGTH + 20);
}' file
amit_g
fuente
2
Funciona bien incluso con archivos algo más grandes también
Touko
44
¿Cómo puedes usar esto para encontrar múltiples coincidencias por línea?
koox00
1
¿Cuál es el significado del primer número en los pares entre corchetes? ¿Como los 0 en "grep -E -o". {0,5} test_pattern. {0,5} "test.txt"?
Lew Rockwell Fan
Es realmente más rápido pero no tan preciso como la respuesta de @ekse.
Abdollah
24

Quieres decir así:

grep -o '.\{0,20\}test_pattern.\{0,20\}' file

?

Eso imprimirá hasta veinte caracteres a cada lado de test_pattern. La \{0,20\}notación es como *, pero especifica de cero a veinte repeticiones en lugar de cero o más. -oDice que muestre solo la coincidencia en sí, en lugar de la línea completa.

ruakh
fuente
Este comando no funciona para mí:grep: Invalid content of \{\}
Alexander Pravdin
0

Con gawk, puede usar la función de coincidencia:

    x="hey there how are you"
    echo "$x" |awk --re-interval '{match($0,/(.{4})how(.{4})/,a);print a[1],a[2]}'
    ere   are

Si está de acuerdo con perluna solución más flexible: a continuación se imprimirán tres caracteres antes del patrón, seguidos por el patrón real y luego 5 caracteres después del patrón.

echo hey there how are you |perl -lne 'print "$1$2$3" if /(.{3})(there)(.{5})/'
ey there how

Esto también se puede aplicar a palabras en lugar de solo caracteres. A continuación se imprimirá una palabra antes de la cadena coincidente real.

echo hey there how are you |perl -lne 'print $1 if /(\w+) there/'
hey

A continuación se imprimirá una palabra después del patrón:

echo hey there how are you |perl -lne 'print $2 if /(\w+) there (\w+)/'
how

A continuación se imprimirá una palabra antes del patrón, luego la palabra real y luego una palabra después del patrón:

echo hey there how are you |perl -lne 'print "$1$2$3" if /(\w+)( there )(\w+)/'
hey there how
PAGS....
fuente
0

Puede usar regexp grep para encontrar + segundo grep para resaltar

echo "some123_string_and_another" | grep -o -P '.{0,3}string.{0,4}' | grep string

23_cadena_y

ingrese la descripción de la imagen aquí

Andrew Zhilin
fuente
0

Nunca recordaré fácilmente estos modificadores de comandos crípticos, así que tomé la respuesta principal y la convertí en una función en mi ~/.bashrcarchivo:


cgrep() {
    # For files that are arrays 10's of thousands of characters print.
    # Use cpgrep to print 30 characters before and after search patttern.
    if [ $# -eq 2 ] ; then
        # Format was 'cgrep "search string" /path/to/filename'
        grep -o -P ".{0,30}$1.{0,30}" "$2"
    else
        # Format was 'cat /path/to/filename | cgrep "search string"
        grep -o -P ".{0,30}$1.{0,30}"
    fi
} # cgrep()

Esto es lo que parece en acción:

$ ll /tmp/rick/scp.Mf7UdS/Mf7UdS.Source

-rw-r--r-- 1 rick rick 25780 Jul  3 19:05 /tmp/rick/scp.Mf7UdS/Mf7UdS.Source

$ cat /tmp/rick/scp.Mf7UdS/Mf7UdS.Source | cgrep "Link to iconic"

1:43:30.3540244000 /mnt/e/bin/Link to iconic S -rwxrwxrwx 777 rick 1000 ri

$ cgrep "Link to iconic" /tmp/rick/scp.Mf7UdS/Mf7UdS.Source

1:43:30.3540244000 /mnt/e/bin/Link to iconic S -rwxrwxrwx 777 rick 1000 ri

El archivo en cuestión es una línea continua de 25K y es imposible encontrar lo que está buscando usando regular grep.

Observe las dos formas diferentes en que puede llamar a cgrepese grepmétodo paralelo .

Hay una forma "niftier" de crear la función donde "$ 2" solo se pasa cuando se establece, lo que ahorraría 4 líneas de código. Sin embargo, no lo tengo a mano. Algo así como ${parm2} $parm2. Si lo encuentro, revisaré la función y esta respuesta.

WinEunuuchs2Unix
fuente