Imprimir con sed o awk una línea siguiendo un patrón coincidente
Pregunta: Me gustaría imprimir una sola línea directamente después de una línea que contiene un patrón coincidente.
Mi versión de sed
no adoptará la siguiente sintaxis (falla +1p
), lo que parecería una solución simple:
sed -n '/ABC/,+1p' infile
Supongo awk
que sería mejor realizar un procesamiento multilínea, pero no estoy seguro de cómo hacerlo.
Nunca utilice la palabra "patrón" en este contexto ya que es ambigua. Utilice siempre "cadena" o "expresa regular" (o en shell "patrón global"), lo que realmente quiera decir. Consulte ¿Cómo encuentro el texto que coincide con un patrón? para más información sobre eso.
La respuesta específica que desea es:
awk 'f{print;f=0} /regexp/{f=1}' file
o especializando la solución más general del enésimo registro después de una expresión regular (modismo "c" a continuación):
awk 'c&&!--c; /regexp/{c=1}' file
Los siguientes modismos describen cómo seleccionar un rango de registros dada una expresión regular específica para que coincida:
a) Imprima todos los registros de alguna expresión regular:
awk '/regexp/{f=1}f' file
b) Imprima todos los registros después de alguna expresión regular:
awk 'f;/regexp/{f=1}' file
c) Imprima el enésimo registro después de alguna expresión regular:
awk 'c&&!--c;/regexp/{c=N}' file
d) Imprima todos los registros excepto el enésimo registro después de alguna expresión regular:
awk 'c&&!--c{next}/regexp/{c=N}1' file
e) Imprima los N registros después de alguna expresión regular:
awk 'c&&c--;/regexp/{c=N}' file
f) Imprima todos los registros excepto los N registros después de alguna expresión regular:
awk 'c&&c--{next}/regexp/{c=N}1' file
g) Imprima los N registros de alguna expresión regular:
awk '/regexp/{c=N}c&&c--' file
Cambié el nombre de la variable de "f" para "encontrado" a "c" para "recuento" cuando correspondía, ya que eso expresa mejor lo que realmente ES la variable.
f
es la abreviatura de found
. Es un indicador booleano que establezco en 1 (verdadero) cuando encuentro una cadena que coincide con la expresión regular regexp en la entrada ( /regexp/{f=1}
). El otro lugar que ve f
por sí solo en cada secuencia de comandos es que se prueba como una condición y, cuando es verdadero, hace que awk ejecute su acción predeterminada de imprimir el registro actual. Entonces, los registros de entrada solo obtienen salida después de que veamos la expresión regular y la establezcamos f
en 1/verdadero.
c && c-- { foo }
significa "si c
es distinto de cero, disminúyelo y si aún es distinto de cero, ejecútelo foo
", por lo que si c
comienza en 3, se reducirá a 2 y luego foo
se ejecutará, y en la siguiente línea de entrada c
ahora es 2, por lo que se reducirá a 1 y luego foo
se ejecutará nuevamente, y en la siguiente línea de entrada c
ahora es 1, por lo que se reducirá a 0, pero esta vez foo
no se ejecutará porque 0 es una condición falsa. Lo hacemos c && c--
en lugar de simplemente realizar pruebas para c-- > 0
no encontrarnos con un caso con un archivo de entrada enorme donde c
llega a cero y continúa disminuyendo con tanta frecuencia que se ajusta y vuelve a ser positivo.
Es la línea después de ese partido lo que te interesa, ¿verdad? En sed, eso podría lograrse así:
sed -n '/ABC/{n;p}' infile
Alternativamente, la opción A de grep podría ser lo que estás buscando.
-A NUM, Print NUM lines of trailing context after matching lines.
Por ejemplo, dado el siguiente archivo de entrada:
foo
bar
baz
bash
bongo
Podrías usar lo siguiente:
$ grep -A 1 "bar" file
bar
baz
$ sed -n '/bar/{n;p}' file
baz
Necesitaba imprimir TODAS las líneas después del patrón (ok Ed, REGEX), así que me decidí por esta:
sed -n '/pattern/,$p' # prints all lines after ( and including ) the pattern
Pero como quería imprimir todas las líneas DESPUÉS (y excluir el patrón)
sed -n '/pattern/,$p' | tail -n+2 # all lines after first occurrence of pattern
Supongo que en tu caso puedes agregar un head -1
al final.
sed -n '/pattern/,$p' | tail -n+2 | head -1 # prints line after pattern
Y realmente debería incluir el comentario de tlwhitec en esta respuesta (ya que su enfoque sed-strict es más elegante que mis sugerencias):
sed '0,/pattern/d'
El script anterior elimina cada línea comenzando con la primera y terminando en (e incluyendo) la línea que coincide con el patrón. Todas las líneas posteriores se imprimen.
Versión awk:
awk '/regexp/ { getline; print $0; }' filetosearch