Come si mantengono solo le ultime n righe di un file di registro?


18

Una sceneggiatura che ho scritto fa qualcosa e, alla fine, aggiunge alcune righe al proprio file di registro. Vorrei mantenere solo le ultime n righe (diciamo, 1000 righe) del file di registro. Questo può essere fatto alla fine dello script in questo modo:

tail -n 1000 myscript.log > myscript.log.tmp
mv -f myscript.log.tmp myscript.log

ma esiste una soluzione più pulita ed elegante? Forse realizzato tramite un singolo comando?


logrotateè la soluzione elegante
Ipor Sircer,

1
Ci ho pensato, ma la configurazione di logrotate sarebbe più lunga dello script stesso ...
dr01

Se logrotate è eccessivo, la soluzione è tanto elegante quanto possibile. Con sed / awk potresti essere in grado di farlo in una riga ma non senza un file temporaneo internamente, quindi probabilmente non è più efficiente e probabilmente meno leggibile.
Kba sta con Monica il

Risposte:


28

È possibile in questo modo, ma come altri hanno già detto, l'opzione più sicura è la generazione di un nuovo file e quindi uno spostamento di quel file per sovrascrivere l'originale.

Il metodo seguente carica le linee in BASH, quindi a seconda del numero di linee da tail, ciò influenzerà l'utilizzo della memoria della shell locale per memorizzare il contenuto delle linee di registro.

Quanto segue rimuove anche le righe vuote se dovessero esistere alla fine del file di registro (a causa del comportamento della valutazione BASH "$(tail -1000 test.log)"), quindi non fornisce un troncamento accurato al 100% in tutti gli scenari, ma a seconda della situazione, può essere sufficiente.

$ wc -l myscript.log
475494 myscript.log

$ echo "$(tail -1000 myscript.log)" > myscript.log

$ wc -l myscript.log
1000 myscript.log

Inteligente. Ho contrassegnato questo come risposta accettata in quanto non richiede l'installazione di strumenti aggiuntivi. Vorrei poter accettare sia la tua che la risposta di John1024.
dr01,

La tua chiamata. Ho votato a favore della soluzione spugna perché non lo sapevo ed è garantito che non si scherza con le righe di registro vuote. Questa soluzione ha il potenziale per farlo, a seconda del contenuto del file di registro.
Parkamark,

Questa soluzione ha una condizione di gara. Se sei sfortunato, il reindirizzamento -in- il file avviene prima della lettura -da- il file e si finisce con un file vuoto.
Coroos

21

L'utilità spongeè progettata proprio per questo caso. Se lo hai installato, puoi scrivere le tue due righe:

tail -n 1000 myscript.log | sponge myscript.log

Normalmente, leggere da un file nello stesso momento in cui ci si scrive non è affidabile. spongerisolve questo problema non scrivendo myscript.logfino a quando dopo tailaver finito di leggerlo e aver terminato la pipe.

Installare

Per installare spongesu un sistema simile a Debian:

apt-get install moreutils

Per installare spongesu un sistema RHEL / CentOS, aggiungere il repository EPEL e quindi:

yum install moreutils

Documentazione

Da man sponge:

spongelegge l'input standard e lo scrive nel file specificato. A differenza del reindirizzamento della shell, spongeassorbe tutto il suo input prima di scrivere il file di output. Ciò consente di costruire pipeline che leggono e scrivono nello stesso file.


2
+1 Grazie, non lo sapevo sponge. Molto utile per tutti coloro che hanno imparato nel modo più duro che non puoi fare sort importantfile.txt > importantfile.txt:)
dr01

4

sicuramente "tail + mv" è molto meglio! Ma per Gnu Sed possiamo provarci

sed -i -e :a -e '$q;N;101,$D;ba' log

3

Per la cronaca, con edte potresti fare qualcosa del genere

ed -s infile <<\IN
0r !tail -n 1000 infile
+1,$d
,p
q
IN

Questo si apre infilee rfinisce nell'output di tail -n 1000 infile(cioè inserisce quell'output prima della prima riga) e quindi elimina da quella che era inizialmente la prima riga fino alla fine del file. Sostituisci ,pcon wper modificare il file sul posto.
Tieni presente, tuttavia, che le edsoluzioni non sono adatte per file di grandi dimensioni.


0

Quello che puoi fare nel tuo script è implementare la logica della rotazione dei log. Esegui tutto il log tramite una funzione:

log()
{
   ...
}

Questa funzione, in primo luogo, fa qualcosa del tipo:

printf "%s\n" "$*" >> logfile

quindi controlla la dimensione del file o in qualche modo decide che il file richiede rotazione. A quel punto, il file logfile.1, se esiste, viene rimosso, il file logfile.0, se esiste, viene rinominato logfile.1e logfilerinominato logfile.0.

Decidere se ruotare potrebbe essere basato su un contatore gestito nello script stesso. Quando raggiunge 1000, viene riportato a zero.

Se è sempre necessario tagliare sempre rigorosamente a 1000 righe, lo script potrebbe contare il numero di righe nel file di registro all'avvio e inizializzare il contatore di conseguenza (o se il conteggio soddisfa già o supera 1000, eseguire immediatamente la rotazione).

Oppure potresti ottenere le dimensioni, ad esempio con wc -c logfilee fare la rotazione in base al superamento di una determinata dimensione. In questo modo il file non deve mai essere scansionato per determinare la condizione.


0

Ho usato, invece di mv, il cpcomando per ottenere che tu sia in grado di avere alcuni file di registro sul posto dove è in esecuzione un software. Forse nella diversa home directory dell'utente o nella directory dell'app e tutti i log sono in un unico posto come hardlink. Se si utilizza il mvcomando si perde il collegamento reale. Se si utilizza il cpcomando invece si manterrà questo collegamento reale.

il mio codice è qualcosa del tipo:

TMP_FILE="$(mktemp "${TMPFILENAME}.XXX")"

for FILE in "${LOGFILE_DIR}"/* ; do
    tail -n $MAXLINES "${FILE}" > "${TMP_FILE}"
    if [ $(ls -g "${TMP_FILE}" | awk '{print $4}') -lt $(ls -g "${FILE}" | awk '{print $4}') ] ; then
        cp "${TMP_FILE}" "${FILE}"
    fi
done   

Quindi, se i file si trovano sullo stesso filesystem, potresti dare anche alcuni diritti diversi agli utenti e nel ${LOGFILE_DIR}modificarne la lunghezza come faccio io.

Se è il mv comando perdi il collegamento tra i file e quindi il tuo secondo file non è più connesso al primo - forse posto altrove.

Se dall'altra parte non permetti a qualcuno di cancellare il file, i tuoi registri rimarranno insieme e sarai ben controllato tramite il tuo script.

logrotateforse più bello. Ma sono contento di questa soluzione.

Non essere disturbato dal "", ma nel mio caso ci sono alcuni file con spazi e altre lettere speciali in e Se non faccio il "" in giro o il {} l'intero lotto non funziona bene.

Per esempio c'è un Dir in cui i file più vecchi ottiene automatizzati zip in una OLDFILE.zipe tutto ciò che viene compresso è così elencati nel file .zip_login modo che il .zip_logè in questo Dir pure ma nel LOGFILE_DIRquando ci sono andata con:

ln .zip_log "${LOGFILE_DIR}/USER_ZIP_log"

il file uguale in quanto è un collegamento reale.

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.