¿Cómo buscar texto en un archivo y mostrar el párrafo que tiene el texto?


24

A continuación se muestra el texto en el archivo:

Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

Necesito grep para "42B" y obtener el resultado del texto anterior como:

Pseudo name=Apple
Code=42B
state=fault

¿Alguien tiene idea de cómo lograr esto usando grep/ awk/ sed?


Etiquetó esta pregunta con solo "grep". ¿Entonces solo buscas soluciones "grep"? En la pregunta, especifique awk & sed también. ¿Podemos agregar esas etiquetas? No estaba seguro de tu intención cuando edité la pregunta anoche.
slm

Respuestas:


38

Con awk

awk -v RS='' '/42B/' file

RS=cambia el separador de registro de entrada de una nueva línea a líneas en blanco. Si algún campo en un registro contiene /42B/imprimir el registro.

''(la cadena nula) es un valor mágico utilizado para representar líneas en blanco según POSIX :

Si RS es nulo, los registros están separados por secuencias que consisten en una <newline>o más líneas en blanco, las líneas en blanco iniciales o finales no darán como resultado registros vacíos al principio o al final de la entrada, y a <newline>siempre será un separador de campo, no importa cuál sea el valor de FS .

Los párrafos de salida no se separarán ya que el separador de salida sigue siendo una nueva línea. Para asegurarse de que haya una línea en blanco entre los párrafos de salida, establezca el separador de registro de salida en dos líneas nuevas:

awk -v RS='' -v ORS='\n\n' '/42B/' file

1
+1 para una solución elegante. Sin embargo, no es necesario redirigir el archivo ...
jasonwryan

los dedos estaban en piloto automático.
llua

2
@jasonwryan, a menos que necesite acceso al nombre del archivo dentro de awk ( FILENAME), no es una mala idea usar la redirección, ya que evita problemas para el nombre de archivo que contiene =o comienza con -(o es -), genera mensajes de error consistentes y evita ejecutar awko ejecutar otras redirecciones si el archivo de entrada no se puede abrir.
Stéphane Chazelas

14

Suponiendo que los datos están estructurados de modo que siempre sea la línea anterior y posterior a la que desea, puede usar los interruptores grep -A(after) y -B(before) para indicar que incluya la línea 1 antes del partido y la línea 1 después:

$ grep -A 1 -B 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Si desea las mismas líneas numéricas antes y después del término de búsqueda, puede usar el -Cinterruptor (contexto):

$ grep -C 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Si desea ser más estricto al hacer coincidir las líneas múltiples, puede usar la herramienta pcregrep, para hacer coincidir un patrón en varias líneas:

$ pcregrep -M 'Pseudo.*\n.*42B.*\nstate.*' sample.txt
Pseudo name=Apple
Code=42B
state=fault

El patrón anterior coincide de la siguiente manera:

  • -M - líneas múltiples
  • 'Pseudo.*\n.*42B.*\nstate.*'- coincide con un grupo de cadenas donde la primera cadena comienza con la palabra "Pseudo"seguida de cualquier carácter hasta el final de la línea \n, seguido de cualquier carácter hasta la cadena "42B"seguido de cualquier carácter hasta el otro extremo de la línea ( \n), seguido de la cadena "state"seguido de cualquier personaje.

55
-C(contexto) puede usarse como un acceso directo, si -Ay -Bson lo mismo.
David Baggerman

@DavidBaggerman - gracias. Lo agregó a la respuesta.
slm

¿Por qué el voto negativo? Esto responde a la pregunta.
slm

4

Probablemente haya una manera similar de hacerlo con awk, pero en perl:

cat file | perl -ne 'BEGIN { $/="\n\n" }; print if $_ =~ /42B/;'

Básicamente, eso dice dividir el archivo en fragmentos delimitados por líneas en blanco, luego solo imprimir esos fragmentos que coincidan con su expresión regular.


10
Esto se puede simplificar usando opciones y shorthands, y perdiendo el uso inútil decat ; perl -00 -ne 'print if /42B/' file
tripleee

4

El grepde algunos sabores de Unix tiene la -pbandera de "párrafo". Sé que AIX sí .

grep -p 42B <myfile>

haría exactamente lo que estás pidiendo allí. YMMV y GNU grep no tienen esta bandera.


Tener la bandera -p sería maravilloso. Especialmente si se usa junto con -v para que pueda excluir párrafos enteros de la salida.
IllvilJa

2

Otra solución perl, sin una línea vacía final:

perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo

Ejemplo

% perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo
Pseudo name=Apple
Code=42B
state=fault

% cat foo
Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

1
O más corto (y por lo tanto más fácil de leer), como triplee escribió en un comentario: perl -00 -ne 'print if /42B/' file.
mivk
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.