Mostrar todo el archivo hasta el partido


71

grep --before-context 5 muestra 5 líneas antes del partido.

Quiero mostrar todo antes del partido.
Hacerlo grep --before-context 99999999funcionaría pero no es muy ... profesional.

¿Cómo mostrar todo el archivo hasta el partido?

Respuestas:


95

Sed es mejor para eso.

Solo haz:

sed '/PATTERN/q' FILE

Funciona así:

Para cada línea, buscamos si coincide /PATTERN:

  • en caso afirmativo, lo imprimimos y lo dejamos
  • de lo contrario, lo imprimimos

Esta es la solución más eficiente, porque tan pronto como se ve PATTERN, se cierra. Sin q, sed continuaría leyendo el resto del archivo y no haría nada con él. Para archivos grandes puede hacer la diferencia.

Este truco también se puede usar para emular head:

sed 10q FILE

Acabo de intentarlo, solo muestra la primera línea del archivo ... a pesar de que el partido está en la línea 38.
Nicolas Raoul

Funciona bien para mi. ¿Puedes dar un ejemplo de entrada y salida real? Y el comando que está ejecutando tal cual.
Mikel

Había probado tu comando antes de que lo editaras, era: sed '/ PATTERN / p; q' FILE
Nicolas Raoul

77
¿Qué debo hacer si no quiero imprimir la línea con la coincidencia de patrón?
tommy.carstensen

44
@ tommy.carstensen: sed '/PATTERN/Q' FILEomitirá la línea coincidente. Qes una extensión GNU, por lo que no funcionará con ningún sed.
Alex O

37

sed puede reemplazar la mayor parte de la funcionalidad de grep.

sed -n '1,/<pattern>/ p' <file>

Esto significa imprimir desde la primera línea hasta que el patrón coincida.

Un par de ejemplos de rango

sed -n '/<pattern>/,$ p' <file> # from pattern to end of file
sed -n '/<pattern1>/,/<pattern2>/ p' <file> # from pattern1 to pattern2

3
Este comando es bueno, pero puedes hacerlo mejor. De esta forma, lee todo el archivo, pero es posible salir tan pronto como encuentre una coincidencia.
Mikel

3
¿Qué debo hacer si no quiero imprimir la línea con la coincidencia de patrón?
tommy.carstensen

34

imprimir hasta e incluyendo el partido:

awk '{print} /pattern/ {exit}' filename
sed '/pattern/q' filename

imprimir hasta PERO SIN incluir el partido:

awk '/pattern/ {exit} {print}' filename
sed '/pattern/Q' filename

11
QEs genial pero afaik específico de GNU, sed -n '/pattern/!p;//q'sería más portátil.
don_crissti

@don_crissti: deberías darle una respuesta, creo que es buena (: aunque tengo un poco de curiosidad por cómo funciona. Creo que !se paplica a las líneas que no coinciden pattern, pero luego //qme confunde ...
jwd

2
@don_crissti: ah, lo tengo - //significa "expresión regular anterior" (pensé que significaba "coincidir con la cadena vacía"). Creo que una versión más corta de la misma solución es sed -n '/pattern/q;p:?
jwd

@jwd: de hecho, eso es más corto. 👍
don_crissti

1

Los siguientes grepmétodos puros de GNU no son eficientes.

Busque todo hasta la primera instancia de la cadena " foo " en la barra de archivos , utilizando tres greps:

grep -m 1 -B $(grep -n -m 1 foo bar | grep -o '^[0-9]*') foo bar

Coincidencia hasta la última instancia de " foo ":

grep -oPz "(?s)[^\n]*${s}.*?\n.*?foo.*?\n" bar

Nota: los detalles sobre el último grepse pueden encontrar en: Regex (grep) para la búsqueda multilínea necesaria .


¿Por qué alguien querría usar 7 greps (+ pcre) cuando se trata simplemente de ejecutar una sola sedinvocación: sed 'x;/./G;//!x;/foo/p;//s/.*//;x;d'??
don_crissti

@don_crissti, su sedcódigo parece tener su propia respuesta, o podría agregarse a uno de los otros. Re 7 greps: Porque no hubo greprespuesta ... (además, la respuesta ayuda a mostrar por qué no)
agc

No es mi código, simplemente haga clic en él ... Incluso si ese fuera mi código, no responde la Q aquí, así que no lo publicaría como respuesta.
don_crissti

1

Agregando a la respuesta de Mikel arriba ...


Para imprimir todas las líneas hasta, pero sin incluir , la primera línea que FILEcontiene PATTERN, intente:

  • sed '/.*PATTERN.*/{s///;q;}' FILE

Esto coincide con la línea completa que contiene el patrón, lo reemplaza con una línea en blanco y luego se cierra sin procesar el resto del archivo.


Posdata:

La forma más fácil / clara que se me ocurrió para evitar imprimir una nueva línea adicional al final (sin involucrar a otra herramienta), fue ejecutar sed nuevamente y eliminar la nueva línea final:

sed '/.*PATTERN.*/{s///;q;}' FILE | sed '$d'

... y dado que ahora estamos eliminando esa línea de todos modos, nuestro trabajo anterior es redundante y podemos simplificarlo para:

sed '/PATTERN/q' FILE | sed '$d'

La respuesta de Glenn, y mi comentario allí, muestran cómo hacerlo con una sola sedinvocación.
don_crissti

(Gracias por eso -. Vi sus comentarios sobre la respuesta del AGC, pero tampoco se perdió el otro o simplemente desnatada porque mi cerebro no le gustan los dobles negativos) Puesto que yo estaba usando esto en tanto tcshy un bashalias, que necesita para asegurarse de que tenía una solución de una línea relativamente concisa que funcionaba tanto en estándar como en GNU sed(para portabilidad); todos los requisitos que su contribución bien pudo haber cumplido. Como alguien que usa sed muy raramente, mi requisito más importante era algo que pudiera entender rápidamente cuando quiero editarlo fácilmente o cambiar su propósito dentro de años.
Jim Grisham

1

Para las personas que eligen recordar solo las herramientas básicas en el trabajo diario y están dispuestas a aceptar soluciones menos elegantes y menos eficientes:

head -n $(grep -n pattern filename | cut -d: -f1) filename

Si este comando es para un script, buscaré soluciones más elegantes (y posiblemente eficientes). Si este es un comando de una sola vez o un script de descarte, no me importa


1
Buena idea, pero tres comandos cuando uno lo hará.
Mikel

1
Conocer lo básico es muy bueno. Sin embargo, conocer la herramienta adecuada para el trabajo es mejor.
soulmerge

Si este comando es para un script, buscaré soluciones más elegantes (y posiblemente eficientes). Si este es un comando de una sola vez (o un script de descarte), entonces no me importa.
lesmana

0

También puedes usar uno de los siguientes

tac ./test | grep -B $(cat ./test | wc -l) -m 1 'pattern'|tac 

o

tac ./test |head -n $(tac ./test | grep -n 'pattern' | cut -d: -f1 | head -n 1)|tac

o

tac ./test |sed ':a;N;$!ba;s/\n/'"pattern"'/g' | sed 's/'"patternpattern"'/\n/g'|head -n 1|sed 's/'"pattern"'/\n/g'|tac

La primera opción es muy similar a lo que sugirió el OP, solo se asegura de que muestre suficientes líneas antes del contexto contando las líneas en el archivo

La segunda opción busca el número de línea de la primera coincidencia (también puede cambiar eso cambiando la 'cabeza' interna) y luego usa cabeza en ese número

La última opción reemplaza todas las líneas nuevas con la coincidencia y luego reemplaza dos coincidencias adyacentes con una nueva línea. El resultado de esto es una línea para cada bloque de texto entre dos coincidencias. Después de eso, usa 'head' para elegir la primera línea (que disminuye el bloque de texto hasta la primera coincidencia) y luego vuelve a traducir cada coincidencia a una nueva línea. esta opción solo funciona si el archivo tiene el siguiente formato

pattern
texttexttext
texttexttext texttexttext
texttexttexttexttexttexttexttexttext
pattern
texttexttext
pattern 
texttexttext
texttexttexttexttexttexttexttexttext

Etcétera


2
considere explicar cómo funcionan, especialmente porque ese sedcomando en la parte inferior es algo retorcido.
strugee

la primera opción es muy similar a lo que sugirió el OP, solo se asegura de que muestre suficientes kines antes del contexto contando las líneas en el filtro,
user122778
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.