Risposte:
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
Come funziona:
cat
legge il file chiamato input.log
e lo stampa sul suo flusso di output standard.
Normalmente l'output standard è collegato a un terminale, ma questo piccolo script contiene in |
modo che la shell reindirizzi l'output cat
standard dell'input standard di sed
.
sed
legge i dati (come li cat
produce), li elabora (secondo lo script fornito con l' -e
opzione) e quindi li stampa sul suo output standard. Lo script "s/^/$(date -R) /"
significa sostituire ogni inizio di riga con un testo generato dal date -R
comando (la costruzione generale per il comando di sostituzione è:) s/pattern/replace/
.
Quindi, secondo i >>
bash
reindirizzamenti, l'output di sed
un file chiamato output.log
( >
significa sostituire il contenuto del file e >>
significa aggiungere alla fine).
Il problema è $(date -R)
valutato una volta quando si esegue lo script in modo da inserire il timestamp corrente all'inizio di ogni riga. Il timestamp corrente potrebbe essere lontano da un momento in cui è stato generato un messaggio. Per evitarlo devi elaborare i messaggi mentre vengono scritti nel file, non con un processo cron.
Il reindirizzamento stream standard descritto sopra chiamato pipe . Puoi reindirizzarlo non solo |
tra i comandi nello script, ma attraverso un file FIFO ( detto anche pipe ). Un programma scriverà sul file e un altro leggerà i dati e li riceverà al primo invio.
Scegli un esempio:
$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;
# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo
$ cat foo.log
Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz
Come funziona:
mkfifo
crea una pipa denominata
while true; do sed ... ; done
esegue un ciclo infinito e ad ogni iterazione corre sed
con il reindirizzamento foo.log.fifo
al suo input standard; sed
si blocca in attesa di dati di input, quindi elabora un messaggio ricevuto e lo stampa sull'output standard reindirizzato a foo.log
.
A questo punto devi aprire una nuova finestra del terminale perché il loop occupa il terminale corrente.
echo ... > foo.log.fifo
stampa un messaggio sul suo output standard reindirizzato al file fifo e lo sed
riceve, lo elabora e scrive su un file normale.
La nota importante è il fifo proprio come qualsiasi altra pipe non ha senso se uno dei suoi lati non è collegato a nessun processo. Se si tenta di scrivere su una pipe, il processo corrente si bloccherà fino a quando qualcuno non leggerà i dati dall'altra parte della pipe. Se si desidera leggere da una pipe, il processo si bloccherà fino a quando qualcuno non scriverà i dati nella pipe. Il sed
ciclo nell'esempio sopra non fa nulla (dorme) fino a quando non lo fai echo
.
Per la tua situazione particolare, devi solo configurare l'applicazione per scrivere i messaggi di registro nel file FIFO. Se non è possibile configurarlo, è sufficiente eliminare il file di registro originale e creare un file FIFO. Ma nota ancora che se il sed
loop morirà per qualche motivo - il tuo programma verrà bloccato quando si tenta di accedere write
al file fino a quando qualcuno non lo farà read
dal FIFO.
Il vantaggio è il timestamp corrente valutato e allegato a un messaggio mentre il programma lo scrive nel file.
tailf
Per rendere più indipendente la scrittura nel registro e l'elaborazione, è possibile utilizzare due file regolari con tailf
. Un'applicazione scriverà un messaggio in un file non elaborato e altri processi leggeranno nuove righe (segui per scrivere in modo asincrono) ed elaboreranno i dati con la scrittura nel secondo file.
Facciamo un esempio:
# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;
$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log
$ cat bar.log
Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz
Come funziona:
Eseguire il tailf
processo che seguirà le scritture bar.raw.log
e stamparle sull'output standard reindirizzato al while read ... echo
ciclo infinito . Questo ciclo esegue due azioni: leggere i dati dall'input standard in una variabile buffer chiamata line
e quindi scrivere il timestamp generato con i seguenti dati bufferizzati nel file bar.log
.
Scrivi alcuni messaggi al bar.raw.log
. Devi farlo in una finestra terminale separata perché la prima sarà occupata dalla tailf
quale seguirà le scritture e farà il suo lavoro. Abbastanza semplice.
I pro è che la tua applicazione non si bloccherebbe se uccidi tailf
. I contro sono timestamp meno accurati e file di registro duplicati.
tailf
, ha aggiunto il modo giusto di usarlo. In realtà il modo in cui tailf
sembra essere più elegante, ma ho lasciato la quindicesima via con la speranza che possa essere utile per qualcuno.
È possibile utilizzare lo ts
script perl da moreutils
:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Modificato dalla risposta di Dmitry Vasilyanov.
Nello script bash, puoi reindirizzare e avvolgere l'output con timestamp riga per riga al volo.
Quando usare:
tailf
per il file di registro come ha detto Dmitry Vasilyanov.Un esempio chiamato foo.sh
:
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"
E il risultato:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
Come funziona
exec &>
Reindirizza stdout e stderr nello stesso posto>( ... )
pipe emette un comando interno asincronoPer esempio:
timestamp pipe e accedere al file
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
Oppure stampa il timestamp e accedi a stdout
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
quindi salvarli /etc/crontab
nell'impostazione
* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
Ho usato in ts
questo modo per ottenere una voce con un timestamp in un registro errori per uno script che utilizzo per riempire i cactus con le statistiche di un host remoto.
Per testare i cactus utilizzo rand
per aggiungere alcuni valori casuali che utilizzo per i grafici della temperatura per monitorare la temperatura dei miei sistemi.
Pushmonstats.sh è uno script che raccoglie le statistiche di temperatura del sistema del mio PC e le invia a un Raspberry Pi su cui viene eseguito Cacti. Qualche tempo fa, la rete era bloccata. Ho avuto solo timeout SSH nel mio registro errori. Sfortunatamente, nessuna voce temporale nel registro. Non sapevo come aggiungere un timestamp a una voce del registro. Quindi, dopo alcune ricerche su Internet, mi sono imbattuto in questo post e questo è quello che ho fatto usando ts
.
Per testarlo, ho usato un'opzione sconosciuta a rand
. Che ha dato un errore a stderr. Per catturarlo, lo reindirizzo a un file temporaneo. Quindi uso cat per mostrare il contenuto del file e inserirlo ts
, aggiungere un formato orario che ho trovato in questo post e infine registrarlo nel file di errore. Quindi deseleziono il contenuto del file temporaneo, altrimenti ottengo doppie voci per lo stesso errore.
crontab:
* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err
Ciò fornisce quanto segue nel mio registro errori:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
Forse questo non è un modo molto elegante per farlo, ma funziona. Mi chiedo se esista un approccio più elegante.