Como usar o sed para manipular continuamente a saída de streaming?

13

Estou montando uma apresentação para um público não técnico. Eu tenho um programa em execução no bash que gera um fluxo contínuo de valores, alguns dos quais são importantes. Gostaria de destacar os resultados importantes à medida que são exibidos para que o público possa ter uma idéia de sua frequência. O problema é que não consigo sedoperar em um fluxo em execução. Funciona bem se eu colocar os resultados em um arquivo, como em:

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

Mas se eu tentar a mesma coisa na saída em execução, assim:

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

sedfaz nada. Alguma ideia?

Como Lambert foi útil o suficiente para apontar, o fato de eu dizer que sednão faz nada era vago. O que está acontecendo é que o programa gera stdout(tenho certeza de que não está gravando stderr) como faria normalmente, mesmo que seja canalizado sed.

A questão parece ser que o comando chama um segundo programa, que emite para stdout. Existem algumas linhas impressas pelo primeiro programa; estes eu posso editar. Depois, há um fluxo de valores impresso pelo segundo programa; estes eu não posso editar.

Os métodos Perl e awk também não funcionam.

P Jones
fonte
5
Funciona stdbuf -o0 command | sed "s/some text/some text bolded/"?
FloHimself
3
Com 'sed does nothing', você quer dizer que a substituição não foi feita ou você não tem saída? O comando pode estar escrevendo para stderr em vez de stdout? Se você quiser destacar algo que você pode usarcommand|egrep 'some text|$'
Lambert
O comando está gravando no stdout (e não no stderr)?
Anthon
1
Dado que o texto aparece mais de uma vez, você deve adicionar uma gsubstituição "global" obtida, caso contrário, somente a primeira ocorrência em uma linha será substituída:sed "s/old/new/g"
ph0t0nix
@ ph0t0nix Não, esse não é o problema, mas teria sido um problema abaixo da linha. Obrigado pela dica.
P Jones

Respostas:

12

As chances são de que a saída do comando seja armazenada em buffer. Quando o comando grava em um terminal, o buffer é liberado em cada nova linha, para que você apareça na taxa esperada. Quando o comando grava em um canal, o buffer é liberado apenas quando atinge alguns kilobytes, portanto fica muito atrasado. Assim é o comportamento padrão da biblioteca de entrada / saída padrão.

Para forçar o comando a não armazenar sua saída, você pode usar unbuffer(da expectativa) ou stdbuf(do GNU coreutils).

unbuffer command | sed …
stdbuf -o0 command | sed …
Gilles 'SO- parar de ser mau'
fonte
stdbufnão funcionou (foi mencionado anteriormente, BTW), mas unbufferfuncionou !! Você não tem idéia do quão feliz você me deixou.
P Jones
E obrigado pela explicação concisa. Muito útil.
P Jones
sedem si usa esse buffer (cf post ChennyStar), portanto, os exemplos aqui podem não funcionar, pois sedé o commandto buffer: cat /etc/passwd | unbuffer sedmas sedele possui uma -uopção, portanto greppode ser mais adequado nesses exemplos. Muito obrigado pela sua informação de fundo! Ótima resposta!
Math
4

sed tem uma opção para isso:

-u, --unfuffered

Que carrega quantidades mínimas de dados dos arquivos de entrada e libera os buffers de saída com mais frequência. Veja man sedpara mais detalhes.

ChennyStar
fonte
1
sedSomente GNU (não BSD sed), e acredito que isso ainda não impediria o buffer do comando no início do pipe. Mas bom mencionar isso. :)
Caractere curinga
A página de manual do stdbuf afirma que "Se o COMMAND ajustar o buffer de seus fluxos padrão ('tee' faz por exemplo), isso substituirá as alterações correspondentes por 'stdbuf'". Parece que o sed pode se encaixar nessa categoria, mas a bandeira '-u' o tornará útil em uma cadeia de tubos sem buffer.
22418 MattSmith
2

Eu usaria awk

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

Onde

  • /some important stuff/ selecione uma linha importante, como no sed
  • printf "%c[31m%s%c[0m\n",27,$0,27 ; imprimir em vermelho
    • use 32,33 para verde, amarelo ...
    • $ 1, $ 2, podem ser usados ​​para selecionar um campo específico
  • outra linha é apenas impressa 'como está'

o ponto principal é que commanddeve liberar as linhas, mas esse deve ser o caso se você tiver muita saída.

Archemar
fonte
Nota: A pergunta não diz que toda a linha deve estar "em negrito", mas apenas "algum texto". Embora seja possível implementar também esse recurso em awk(using sub()or gsub()), no caso dessa substituição primitiva sedé certamente a ferramenta apropriada.
Janis
1

O caminho perl:

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

ou com uma saída contínua:

Um script bash para a saída cont:

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

Teste com:

./cont | perl -pe 's/(stuff)/\x1b[1m${1}\x1b[0m/g'
  • \x1b[1m - intensidade arrojada ou aumentada
  • ${1} - o backreferenze
  • \x1b[0m - redefinir todos os atributos

Resultado:

Algumas coisas
Algumas coisas
Algumas coisas
Algumas coisas

Mais códigos de escape aqui .

AB
fonte