Bagaimana Anda hanya menyimpan n baris terakhir dari file log?

18

Sebuah skrip yang saya tulis melakukan sesuatu dan, pada akhirnya, menambahkan beberapa baris ke file log-nya sendiri. Saya ingin menyimpan hanya n baris terakhir (katakanlah, 1000 baris) dari file log. Ini dapat dilakukan di akhir skrip dengan cara ini:

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

tetapi adakah solusi yang lebih bersih dan elegan? Mungkin dicapai melalui satu perintah?

dr01
sumber
logrotateadalah solusi elegan
Ipor Sircer
1
Saya sudah memikirkannya, tetapi konfigurasi logrotate akan lebih panjang dari skrip itu sendiri ...
dr01
Jika logrotate berlebihan, solusi Anda akan seelegan itu. Dengan sed / awk Anda mungkin dapat melakukannya dalam satu baris tetapi tidak tanpa file temp secara internal, jadi itu mungkin tidak lebih efisien dan mungkin kurang dapat dibaca.
Kba berdiri bersama Monica

Jawaban:

28

Mungkin seperti ini, tetapi seperti yang orang lain katakan, opsi teraman adalah pembuatan file baru dan kemudian memindahkan file itu untuk menimpa yang asli.

Metode di bawah ini memuat baris ke BASH, jadi tergantung pada jumlah baris dari tail, itu akan mempengaruhi penggunaan memori shell lokal untuk menyimpan konten dari baris log.

Di bawah ini juga menghapus baris kosong jika ada di akhir file log (karena perilaku BASH mengevaluasi "$(tail -1000 test.log)") sehingga tidak memberikan pemotongan 100% akurat di semua skenario, tetapi tergantung pada situasi Anda, mungkin cukup.

$ wc -l myscript.log
475494 myscript.log

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

$ wc -l myscript.log
1000 myscript.log
parkamark
sumber
Pintar. Saya menandai ini sebagai jawaban yang diterima karena tidak memerlukan instalasi alat tambahan. Saya berharap saya bisa menerima jawaban Anda dan @ John1024.
dr01
Panggilanmu. Saya memutakhirkan solusi spons karena saya tidak tahu tentang hal itu dan dijamin tidak akan mengacaukan dengan baris log kosong. Solusi ini berpotensi untuk melakukan itu, tergantung pada konten file log.
parkamark
21

Utilitas spongeini dirancang hanya untuk kasus ini. Jika Anda menginstalnya, maka dua baris Anda dapat ditulis:

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

Biasanya, membaca dari file pada saat yang sama dengan saat Anda menulis tidak dapat diandalkan. spongemenyelesaikan ini dengan tidak menulis myscript.logsampai setelah tailselesai membacanya dan menghentikan pipa.

Install

Untuk menginstal spongepada sistem seperti Debian:

apt-get install moreutils

Untuk menginstal spongepada sistem RHEL / CentOS, tambahkan repo EPEL dan kemudian lakukan:

yum install moreutils

Dokumentasi

Dari man sponge:

spongemembaca input standar dan menuliskannya ke file yang ditentukan. Tidak seperti pengalihan shell, spongemenyerap semua inputnya sebelum menulis file output. Ini memungkinkan membangun saluran pipa yang membaca dari dan menulis ke file yang sama.

John1024
sumber
2
+1 Terima kasih, saya tidak tahu sponge. Sangat berguna untuk semua orang yang belajar dengan cara yang sulit yang tidak dapat Anda lakukan sort importantfile.txt > importantfile.txt:)
dr01
4

pasti "tail + mv" jauh lebih baik! Tetapi untuk gnu sed kita bisa mencoba

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

Sebagai catatan, dengan edAnda bisa melakukan sesuatu seperti

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

Ini terbuka infiledan rkeluar di output tail -n 1000 infile(yaitu memasukkan output sebelum baris 1) dan kemudian menghapus dari apa yang awalnya baris 1 ke akhir file. Ganti ,pdengan wuntuk mengedit file di tempat.
Perlu diingat bahwa edsolusi tidak cocok untuk file besar.

don_crissti
sumber
0

Apa yang dapat Anda lakukan dalam skrip Anda adalah menerapkan logika rotasi log. Lakukan semua penebangan melalui fungsi:

log()
{
   ...
}

Fungsi ini, pertama, melakukan sesuatu seperti:

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

kemudian, ia memeriksa ukuran file atau entah bagaimana memutuskan bahwa file tersebut memerlukan rotasi. Pada titik itu, filelogfile.1 , jika ada, dihapus, file itu logfile.0, jika ada, diubah namanya menjadi logfile.1dan logfilediubah namanya menjadi logfile.0.

Memutuskan apakah akan memutar dapat didasarkan pada penghitung yang disimpan dalam skrip itu sendiri. Ketika mencapai 1000, itu diatur ulang ke nol.

Jika selalu memangkas dengan ketat menjadi 1000 baris adalah persyaratan, skrip dapat menghitung jumlah baris dalam file log saat dimulai, dan menginisialisasi penghitung yang sesuai (atau jika hitungan sudah memenuhi atau melebihi 1000, lakukan rotasi segera).

Atau Anda bisa memperoleh ukuran, seperti dengan wc -c logfiledan melakukan rotasi berdasarkan melebihi ukuran tertentu. Dengan cara ini file tidak pernah dipindai untuk menentukan kondisinya.

Kaz
sumber
0

Saya memang menggunakan, alih-alih mv, cpperintah untuk mencapainya sehingga Anda dapat memiliki beberapa file log di tempat di mana Perangkat Lunak berjalan. Mungkin di direktori home User yang berbeda atau di dir aplikasi dan memiliki semua log di satu tempat sebagai hardlink. Jika Anda menggunakan mvperintah, Anda kehilangan tautan yang sulit. Jika Anda menggunakan cpperintah, Anda akan menyimpan tautan keras ini.

kode saya adalah sesuatu seperti:

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   

Jadi jika file berada pada Filesystem yang sama Anda dapat memberikan juga beberapa hak berbeda kepada pengguna dan ${LOGFILE_DIR}Anda memodifikasi panjang seperti yang saya lakukan.

Jika itu mv perintah Anda kehilangan hardlink antara file dan file kedua Anda tidak lebih terhubung dengan yang pertama - mungkin ditempatkan beberapa tempat lain.

Jika di tempat lain Anda tidak mengizinkan seseorang untuk menghapus file log Anda tetap bersama dan dikontrol dengan baik melalui skrip Anda sendiri.

logrotatemungkin lebih baik. Tapi saya senang dengan solusi ini.

Jangan diganggu oleh "" tetapi dalam kasus saya ada beberapa file dengan spasi dan huruf khusus lainnya di dalam dan "" Jika saya tidak melakukan "" sekitar atau {} seluruh banyak tidak berfungsi dengan baik.

Sebagai contoh ada Dir di mana file yang lebih lama otomatis dizip menjadi OLDFILE.zipdan segala sesuatu yang di-zip juga tercantum dalam File .zip_logsehingga .zip_logada di Dir ini juga tetapi di LOGFILE_DIRsaya punya dengan:

ln .zip_log "${LOGFILE_DIR}/USER_ZIP_log"

file yang sama dengan itu adalah tautan keras.

Andreas Bartels
sumber