Comando per anteporre una stringa a ciascuna riga?


36

Cerchi qualcosa del genere? Qualche idea?

cmd | prepend "[ERRORS] "

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

C'è un modo per impostarlo per tutti i comandi nella funzione / script bash?
Alexander Mills,

Risposte:


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

ha il vantaggio di usare solo i built-in bash in modo che vengano creati / distrutti meno processi, quindi dovrebbe essere un tocco più veloce di awk o sed.

@tzrik sottolinea che potrebbe anche fare una bella funzione bash. Definendolo come:

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

gli permetterebbe di essere usato come:

cmd | prepend "[ERROR] "

4
Questo in realtà riduce solo il conteggio dei processi di uno. (Ma potrebbe essere più veloce perché non vengono utilizzate sedawk
regexps

A proposito, ero curioso delle prestazioni e qui ci sono i risultati del mio semplice benchmark usando bash, sed e awk. Spingendo circa 1000 righe di testo (output di dmesg) nel file FIFO e poi leggendole in questo modo: pastebin.ca/1606844 Sembra che awk sia il vincitore. Qualche idea sul perché?
Ilya Zakreuski,

1
fai attenzione a eseguire i test di temporizzazione in questo modo - prova a eseguirli in tutti e 6 i diversi ordini e poi fai la media dei risultati. Ordini diversi per mitigare gli effetti della cache di blocco e media per mitigare gli effetti di interruzione / pianificazione in background.
pjz,

Questa domanda è taggata "shell", non "bash".
fiatjaf,

1
Abbastanza facile da avvolgerlo anche in una funzione:function prepend() { while read line; do echo "${1}${line}"; done; }
tzrlk,

46

Prova questo:

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

Saluti


1
Questo ha lo svantaggio che "[ERRORE]" non può essere una variabile, poiché l'intera espressione deve essere racchiusa tra virgolette singole.
user1071136

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

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

Puoi semplicemente uscire dall'ambito delle virgolette per dereferenziare la variabile: cmd | awk '{print "['$V]' " $0}'- questo dovrebbe essere valutato una volta all'inizio, quindi nessun sovraccarico di prestazioni.
Robert,

13

Con tutto il merito di @grawity, sto inviando il suo commento come risposta, in quanto mi sembra la risposta migliore qui.

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

Perché è preferibile alla soluzione bash?
user14645

1
Suppongo che dipenda dal tuo scopo. Se il tuo obiettivo è semplicemente anteporre a ogni riga di un file, questo raggiunge quell'obiettivo con pochissimi caratteri, usando uno strumento molto familiare. Preferisco di gran lunga quello a uno script bash a 10 righe. awkL'one-liner è abbastanza bello, ma penso che più persone hanno familiarità con sedrispetto awk. Lo script bash è buono per quello che fa, ma sembra che stia rispondendo a una domanda che non è stata posta.
Eric Wilson,

La risposta che Pjz ha inviato è anche una bella risposta. Non aggiunge programmi, processi e può essere eseguito un po 'più velocemente.
user14645

3
sed X cmdlegge cmde non lo esegue. O cmd | sed 's/^/[ERROR] /'oppure sed 's/^/[ERROR] /' <(cmd)oppure cmd > >(sed 's/^/[ERROR] /'). Ma attenzione a quest'ultimo. Anche che questo ti permette di accedere il valore di ritorno cmdle sedcorse in background, quindi è probabile che si vede l'uscita dopo cmd finiti. Buono per l'accesso a un file, però. E nota che awkprobabilmente è più veloce di sed.
Tino,

Bello. Questo comando è facilmente aliasato. alias lpad="sed 's/^/ /'". invece di ERRORE inserisco 4 spazi iniziali. Ora, per il trucco magico: ls | lpad | pbcopyanteporrà l'output con 4 spazi che lo contrassegnano come Markdown per il codice , il che significa che si incolla gli appunti ( pbcopy lo prende, su Mac) direttamente in StackOverflow o in qualsiasi altro contesto markdown. Non è stata aliasla risposta awk (al primo tentativo), quindi questo vince. Anche la soluzione while read è alias, ma trovo questa sed più espressiva.
JL Peyret,

8

Ho creato un repository GitHub per eseguire alcuni test di velocità.

Il risultato è:

  • Nel caso generale, awkè il più veloce. sedè un po 'più lento e perlnon è molto più lento di sed. Apparentemente, tutti questi sono linguaggi altamente ottimizzati per l'elaborazione del testo.
  • In situazioni molto speciali, in cui dominano le forcelle, l'esecuzione dello script come kshscript compilato ( shcomp) può risparmiare ancora più tempo di elaborazione. Al contrario, bashè molto lento rispetto agli kshscript compilati .
  • Creare un binario staticamente collegato da battere awknon sembra valere la pena.

Al contrario pythonè molto lento, ma non ho testato un caso compilato, perché di solito non è quello che faresti in un caso di scripting.

Vengono testate le seguenti varianti:

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] $_"'

Due varianti binarie di uno dei miei strumenti (non è ottimizzato per la velocità, però):

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

Python bufferato:

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

E Python senza 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 }'per emettere un timestamp
Tino,


3

Volevo una soluzione che gestisse stdout e stderr, quindi l'ho scritta prepend.she inserita nel mio percorso:

#!/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)

Ora posso solo eseguire prepend.sh "[ERROR]" cmd ..., anteporre "[ERROR]" all'output di cmd, e avere comunque stderr e stdout separati.


Ho provato questo approccio, ma c'era qualcosa che succedeva con quelle >(subshells che non riuscivo a risolvere del tutto. Sembrava che lo script fosse completato e l'output stava arrivando al terminale dopo che il prompt era tornato, il che era un po 'disordinato. Alla fine ho trovato la risposta qui stackoverflow.com/a/25948606/409638
robert
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.