Extrae varias líneas si coinciden

Extrae varias líneas si coinciden

Tengo un comando que genera información sobre todas las ranuras DIMM en bloques como el siguiente:

ID    SIZE TYPE
44    105  SMB_TYPE_MEMDEVICE (type 17) (memory device)

  Manufacturer: NO DIMM
  Serial Number: NO DIMM
  Asset Tag: NO DIMM
  Location Tag: P1-DIMMD1
  Part Number: NO DIMM

  Physical Memory Array: 43
  Memory Error Data: Not Supported
  Total Width: 0 bits
  Data Width: 0 bits
  Size: Not Populated
  Form Factor: 9 (DIMM)
  Set: None
  Rank: Unknown
  Memory Type: 2 (unknown)
  Flags: 0x4
        SMB_MDF_UNKNOWN (unknown)
  Speed: Unknown
  Configured Speed: Unknown
  Device Locator: P1-DIMMD1
  Bank Locator: P0_Node1_Channel0_Dimm0
  Minimum Voltage: 1.20V
  Maximum Voltage: 1.20V
  Configured Voltage: 1.20V

Los bloques comienzan con el ID SIZE TYPEencabezado y terminan con la información de voltaje configurada. El comando genera uno de estos bloques de datos para cada DIMM, separados por una sola línea en blanco cada uno.


Me gustaría poder obtener el bloque de información para una ranura DIMM específica según el Location Tagcampo, pero no estoy seguro de cómo hacerlo. Estoy bastante seguro de que esto se puede hacer, awkpero solo sé cómo imprimir la coincidencia awk '/P1-DIMMD1/'o la línea antes de la coincidencia.awk '/P1-DIMMD1/ {print a}{a=$0}'

¿Alguien sabe cómo podría extraer todo este bloque de datos si Location Tagcoincide con mi búsqueda ( P1-DIMMD1)?

Respuesta1

Lo siguiente coincidirá con la etiqueta proporcionada en la variable de etiqueta:

awk -v tag=P1-DIMMD1 '/ID    SIZE TYPE/ { block = $0; output = 0; next } { block = block "\n" $0 } /Location Tag/ { output = ($0 ~ tag) } /Configured Voltage/ && output { print block }'

El guión AWK es

/ID    SIZE TYPE/ {
  block = $0
  output = 0
  next
}

{ block = block "\n" $0 }

/Location Tag/ { output = ($0 ~ tag) }

/Configured Voltage/ && output { print block }

Acumulamos un bloque en la blockvariable y lo generamos cuando llegamos al final del bloque si vimos la etiqueta correcta en el proceso.

Respuesta2

Tú podríasutilizar ed...y¡Sed, hombre!

Tienes quedesearSin embargo, es mejor usar ed para este, ya que ed quiere operar en un archivo, no como parte de una canalización.

  1. command > dimm-output
  2. wanted=P1-DIMMD1
  3. ed -s dimm-output <<< $'/Location Tag: '"$wanted"$'\n?^ID.*SIZE.*TYPE\n.,/Configured Voltage/p\nq\n' | sed 1,2d

La edcadena de comandos se divide en cuatro \ncomandos separados:

  1. busque hacia adelante, usando /, para el texto "Etiqueta de ubicación: " seguido del valor de la $wantedvariable
  2. busque hacia atrás, usando ?, para el patrón: (inicio de línea), "ID", cualquier cosa, "TAMAÑO", cualquier cosa, "TIPO"
  3. desde esa línea ( .), hasta ( ,) la siguiente línea que coincida con "Voltaje configurado", imprima esas líneas ( p)
  4. dejar de fumar:q

Debido a que ed imprime automáticamente la línea coincidente cuando buscas, solía sedeliminar esas dos líneas aquí.

Respuesta3

Inspirándome en la gran respuesta de @Stephen Kitts, escribí un script un poco más general para realizar la coincidencia de bloques cuando tengo un patrón de inicio y fin específico.

#!/usr/bin/awk -f
BEGIN {
    pstart=ARGV[1];
    pstop=ARGV[2];
    pmatch=ARGV[3];
    ARGV[1]=ARGV[4];
    ARGC=2;
}
$0 ~ pstart { block = $0; output = 0; next }
{ block = block "\n" $0 }
$0 ~ pmatch { output = 1 }
$0 ~ pstop && output { print block; output = 0 }

Uso:match_block START END MATCH [FILE]

./match_block '^ID' 'Configured Voltage' 'Location Tag: P1-DIMMD1' f

o

command | ./match_block '^ID' 'Configured Voltage' 'Location Tag: P1-DIMMD1'

Gracias por sugerir escribir esto awkdirectamente como un guión. Mi script de shell original era:

#!/bin/sh

[ -z "$4" ] && file="-" || file="$4"

awk \
  -v BLOCKSTART_PATTERN="$1" \
  -v BLOCKEND_PATTERN="$2" \
  -v BLOCKMATCH_PATTERN="$3" \
  '
  $0 ~ BLOCKSTART_PATTERN { block = $0; output = 0; next }
  { block = block "\n" $0 }
  $0 ~ BLOCKMATCH_PATTERN { output = 1 }
  $0 ~ BLOCKEND_PATTERN && output { print block; output = 0 }
  ' "$file"

Uso: match_block START END MATCH [FILE].
Si se omite el archivo, stdinse utilizará.

En tu caso:

command | ./match_block '^ID' 'Configured Voltage' 'Location Tag: P1-DIMMD1'

o

./match_block '^ID' 'Configured Voltage' 'Location Tag: P1-DIMMD1' file

información relacionada