¿Cómo usar sed para manipular la salida de transmisión continua?

13

Estoy preparando una presentación para una audiencia no técnica. Tengo un programa que se ejecuta en bash que genera un flujo continuo de valores, algunos de los cuales son importantes. Me gustaría resaltar los resultados importantes a medida que se muestran para que el público pueda tener una idea de su frecuencia. El problema es que no puedo sedoperar en una secuencia en ejecución. Funciona bien si pongo los resultados en un archivo, como en:

cat output.txt | sed "s/some text/some text bolded/"

Pero si intento lo mismo en la salida en ejecución, así:

command | sed "s/some text/some text bolded/"

sedno hace nada. ¿Alguna idea?

Como Lambert fue lo suficientemente útil como para señalar, mi dicho de que sedno hace nada fue vago. Lo que está sucediendo es que el programa sale stdout(estoy bastante seguro de que no está escribiendo stderr) como lo haría normalmente, incluso si se canaliza sed.

El problema parece ser que el comando llama a un segundo programa, que luego sale a stdout. Hay algunas líneas impresas por el primer programa; Estos puedo editar. Luego hay una corriente de valores impresos por el segundo programa; estos no puedo editarlos.

Los métodos Perl y awk tampoco funcionan.

P Jones
fuente
55
Funciona stdbuf -o0 command | sed "s/some text/some text bolded/"?
FloHelf
3
Con 'sed no hace nada' quiere decir que la sustitución no se realiza, ¿o no tiene ningún resultado? ¿El comando podría estar escribiendo en stderr en lugar de stdout? Si desea resaltar algo que podría usarcommand|egrep 'some text|$'
Lambert
¿El comando está escribiendo en stdout (y no en stderr)?
Anthon
1
Dado que el texto aparece más de una vez, debe agregar una gsustitución "global" obtenida, de lo contrario solo se sustituirá la primera aparición en una línea:sed "s/old/new/g"
ph0t0nix
@ ph0t0nix No, ese no es el problema, pero habría sido un problema en el futuro. Gracias por el consejo.
P Jones

Respuestas:

12

Lo más probable es que la salida del comando esté almacenada. Cuando el comando escribe en un terminal, el búfer se vacía en cada nueva línea, por lo que verá que aparece a la velocidad esperada. Cuando el comando escribe en una tubería, el búfer solo se vacía cuando alcanza unos pocos kilobytes, por lo que se retrasa mucho. Por lo tanto, es el comportamiento predeterminado de la biblioteca de entrada / salida estándar.

Para forzar al comando a que no genere un bucle en su salida, puede usar unbuffer(desde esperar) o stdbuf(desde GNU coreutils).

unbuffer command | sed …
stdbuf -o0 command | sed …
Gilles 'SO- deja de ser malvado'
fuente
stdbufno funcionó (se mencionó anteriormente, por cierto), pero lo unbufferhizo !! No tienes idea de lo feliz que me has hecho.
P Jones
Y gracias por la explicación concisa. Muy útil.
P Jones
seden sí usa un búfer de este tipo (véase la publicación de ChennyStar), por lo que los ejemplos aquí pueden no funcionar, ya que sedes commandpara deshacer el búfer: cat /etc/passwd | unbuffer sedpero en sedsí tiene una -uopción, por lo que greppodría ser más adecuado en estos ejemplos. Muchas gracias por tu información de fondo! ¡Gran respuesta!
matemáticas
4

sed tiene una opción para eso:

-u, --unbuffer

Que carga cantidades mínimas de datos de los archivos de entrada y vacía los búferes de salida con mayor frecuencia. Ver man sedpara más detalles.

ChennyStar
fuente
1
GNU sedsolamente (no BSD sed), y creo que esto aún no evitaría el almacenamiento en búfer del comando al comienzo de la tubería. Pero es bueno mencionarlo. :)
Comodín el
La página de manual de stdbuf establece que "Si COMMAND ajusta el almacenamiento en búfer de sus secuencias estándar ('tee' lo hace, por ejemplo), eso anulará los cambios correspondientes con 'stdbuf'". Parece que sed podría caer en esta categoría, pero la bandera '-u' lo hará útil en una cadena de tuberías sin búfer.
MattSmith
2

Usaría awk

  command | awk '/some important stuff/ { printf "%c[31m%s%c[0m\n",27,$0,27 ; next }
  { print ; } '

dónde

  • /some important stuff/ seleccione una línea importante, como en sed
  • printf "%c[31m%s%c[0m\n",27,$0,27 ; imprimir en rojo
    • use 32,33 para verde, amarillo ...
    • $ 1, $ 2, se pueden usar para seleccionar un campo específico
  • otra línea se imprime "tal cual"

el punto clave es que commanddebería enjuagar las líneas, pero ese debería ser el caso si tiene mucha salida.

Archemar
fuente
Nota: La pregunta no dice que toda la línea debe estar "en negrita", sino solo "algo de texto". Si bien es posible implementar también esa característica en awk(usando sub()o gsub()), en el caso de esta sustitución primitiva sedes sin duda la herramienta adecuada.
Janis
1

La manera perl:

command | perl -pe 's/(stuff)/\x1b[1m$1\x1b[0m/g'

o con una salida continua:

Un script bash para la salida cont:

#!/bin/bash
while [ true ]
do
   echo "Some stuff"
done

Prueba con:

./cont | perl -pe 's/(stuff)/\x1b[1m${1}\x1b[0m/g'
  • \x1b[1m - intensidad audaz o aumentada
  • ${1} - la referencia
  • \x1b[0m - restablecer todos los atributos

Salida:

Algunas cosas
Algunas cosas
Algunas cosas
Algunas cosas

Más códigos de escape aquí .

AB
fuente