Comando para acrescentar uma string a cada linha?


36

Procurando algo assim? Alguma ideia?

cmd | prepend "[ERRORS] "

[ERROR] line1 text
[ERROR] line2 text
[ERROR] line3 text
... etc

Existe alguma maneira de configurar isso para todos os comandos na função bash / script?
Alexander Mills

Respostas:


39
cmd | while read line; do echo "[ERROR] $line"; done

tem a vantagem de usar apenas bash built-in para que menos processos sejam criados / destruídos, portanto deve ser um toque mais rápido que awk ou sed.

O @tzrik ressalta que também pode ser uma boa função do bash. Definindo-o como:

function prepend() { while read line; do echo "${1}${line}"; done; }

permitiria que fosse usado como:

cmd | prepend "[ERROR] "

4
Na verdade, isso apenas reduz a contagem do processo em um. (Mas pode ser mais rápido porque há regexps ( sed) ou divisão corda mesmo ( awk) são utilizados.)
grawity

BTW, eu estava curioso sobre o desempenho e aqui estão os resultados do meu benchmark simples usando bash, sed e awk. Enviando cerca de 1000 linhas de texto (saída dmesg) para o arquivo FIFO e depois lendo-as assim: pastebin.ca/1606844 Parece que o awk é o vencedor. Alguma idéia do porquê?
Ilya Zakreuski 9/10/09

1
tenha cuidado ao executar testes de tempo como esse - tente executá-los em todas as 6 ordens diferentes e, em seguida, calcule a média dos resultados. Ordens diferentes para mitigar os efeitos do cache de bloco e média para mitigar os efeitos de interrupção / programação em segundo plano.
pjz

Esta pergunta está marcada com "shell", não "bash".
Fiatjaf

1
Fácil o suficiente para envolvê-la em uma função, bem como:function prepend() { while read line; do echo "${1}${line}"; done; }
tzrlk

46

Tente o seguinte:

cmd | awk '{print "[ERROR] " $0}'

Felicidades


1
Isso tem a desvantagem de que "[ERRO]" não pode ser uma variável, porque toda a expressão deve estar entre aspas simples.
user1071136

4
awk -vT="[ERROR] " '{ print T $0 }'ouawk -vT="[ERROR]" '{ print T " " $0 }'
Tino

2
T="[ERROR] " awk '{ print ENVIRON["T"] $0 }'ouT="[ERROR]" awk '{ print ENVIRON["T"] " " $0 }'
Tino

Você pode simplesmente sair do escopo das aspas para desreferenciar a variável: cmd | awk '{print "['$V]' " $0}'- isso deve ser avaliado uma vez no início, para que não haja sobrecarga de desempenho.
robert

13

Com todo o crédito devido a @grawity, estou enviando o comentário dele como resposta, pois parece a melhor resposta aqui para mim.

sed 's/^/[ERROR] /' cmd

Por que isso é preferível à solução bash?
precisa saber é o seguinte

1
Suponho que depende do seu propósito. Se seu objetivo é simplesmente anexar previamente a cada linha de um arquivo, isso é possível com muito poucos caracteres, usando uma ferramenta muito familiar. Eu prefiro isso a um script bash de 10 linhas. O awkone-liner é bom o suficiente, mas eu acho que as pessoas estão mais familiarizados com sedque awk. O script bash é bom para o que faz, mas parece que está respondendo a uma pergunta que não foi feita.
Eric Wilson

A resposta que o pjz enviou também é uma boa opção. Não possui programas, processos adicionais e pode ser executado um pouco mais rápido.
User14645

3
sed X cmdcmde não o executa. Ou cmd | sed 's/^/[ERROR] /'ou sed 's/^/[ERROR] /' <(cmd)ou cmd > >(sed 's/^/[ERROR] /'). Mas cuidado com o último. Mesmo que este permite que você acesse o valor de retorno cmdas sedcorridas no fundo, por isso é provável que você veja a saída após cmd acabados. É bom para fazer login em um arquivo. E note que awkprovavelmente é mais rápido que sed.
Tino

Agradável. Este comando é facilmente aliasado. alias lpad="sed 's/^/ /'". em vez de ERRO, insiro 4 espaços à esquerda. Agora, o truque de mágica: ls | lpad | pbcopyacrescentará ls à saída com 4 espaços que a marcarão como Markdown para código , o que significa que você cola a área de transferência ( pbcopy agarra-a no macs) diretamente no StackOverflow ou em qualquer outro contexto de marcação. Não foi possível aliasa resposta do awk (na 1ª tentativa), então esta vence. A leitura, enquanto solução é também pseudônimo-capaz, mas eu acho isso sed mais expressivo.
JL Peyret

8

Criei um repositório GitHub para fazer alguns testes de velocidade.

O resultado é:

  • No caso geral, awké o mais rápido. sedé um pouco mais lento e perlnão é muito mais lento que sed. Aparentemente, todos esses são idiomas altamente otimizados para processamento de texto.
  • Em situações muito especiais, onde os garfos dominam, a execução do seu script como um kshscript compilado ( shcomp) pode economizar ainda mais tempo de processamento. Por outro lado, bashé muito lento comparado aos kshscripts compilados .
  • Criar um binário estaticamente vinculado a ser batido awknão vale o esforço.

Por outro lado, pythoné muito lento, mas não testei um caso compilado, porque geralmente não é o que você faria em um caso de script.

As seguintes variantes são testadas:

while read line; do echo "[TEST] $line"; done
while read -r line; do echo "[TEST] $line"; done
while read -r line; do echo "[TEST]" $line; done
while read -r line; do echo "[TEST]" "$line"; done
sed 's/^/[TEST] /'
awk '{ print "[TEST] " $0 }'
awk -vT="[TEST] " '{ print T $0 }'
awk -vT="[TEST]" '{ print T " " $0 }'
awk -vT="[TEST]" 'BEGIN { T=T " "; } { print T $0 }'
T="[TEST] " awk '{ print ENVIRON["T"] $0 }'
T="[TEST]" awk '{ print ENVIRON["T"] " " $0 }'
T="[TEST]" awk 'BEGIN { T=ENVIRON["T"] " " } { print T $0 }'
perl -ne 'print "[TEST] $_"'

Duas variantes binárias de uma das minhas ferramentas (embora não seja otimizada para velocidade):

./unbuffered.dynamic -cp'[TEST] ' -q ''
./unbuffered.static -cp'[TEST] ' -q ''

Buffer em Python:

python -uSc 'import sys
for line in sys.stdin: print "[TEST]",line,'

E Python sem buffer:

python -uSc 'import sys
while 1:
 line = sys.stdin.readline()
 if not line: break
 print "[TEST]",line,'

awk -v T="[TEST %Y%m%d-%H%M%S] " '{ print strftime(T) $0 }'para gerar um carimbo de data
Tino


3

Eu queria uma solução que tratasse stdout e stderr, então escrevi prepend.she coloquei no meu caminho:

#!/bin/bash

prepend_lines(){
  local prepended=$1
  while read line; do
    echo "$prepended" "$line"
  done
}

tag=$1

shift

"$@" > >(prepend_lines "$tag") 2> >(prepend_lines "$tag" 1>&2)

Agora posso apenas executar prepend.sh "[ERROR]" cmd ..., para acrescentar "[ERROR]" à saída de cmde ainda ter stderr e stdout separados.


Eu tentei essa abordagem, mas havia algo acontecendo com esses >(subshells que eu não conseguia resolver. Parecia que o script estava sendo concluído e a saída estava chegando ao terminal depois que o prompt retornou, o que foi um pouco confuso. Eu finalmente veio com a resposta aqui stackoverflow.com/a/25948606/409638
robert
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.