Mengosongkan file tanpa mengganggu penulisan pipa untuk itu


12

Saya memiliki program yang outputnya saya arahkan ke file log:

./my_app > log

Saya ingin menghapus (mis. Kosong) log dari waktu ke waktu (sesuai permintaan) dan mencoba berbagai hal seperti

cat "" > log

Namun sepertinya selalu bahwa pipa asli kemudian terganggu dan program tidak mengarahkan output ke file log lagi.

Apakah ada cara untuk melakukan itu?

Memperbarui

Perhatikan bahwa saya tidak dapat memodifikasi aplikasi yang menghasilkan output. Itu hanya meludahkannya ke stdout dan saya ingin menyimpannya di log sehingga saya bisa memeriksanya saat saya membutuhkannya, dan membersihkannya saat saya mau. Namun saya tidak perlu me-restart aplikasi.


itu sebabnya Anda biasanya menggunakan daemon logging untuk mencatat sesuatu ...
Kiwy

@ Kyiwy dapatkah Anda menjelaskan bagaimana itu akan memecahkan masalah?
bangnab

baik Anda biasanya menggunakan daemon log atau membiarkan aplikasi Anda menangani log, karena menulis sesuatu untuk output dan mengarahkan itu tidak dapat diandalkan. Anda harus melihat pada syslogdataulogrotate
Kiwy

2
Apakah hal-hal berfungsi jika Anda melakukannya ./my_app >> log(untuk memaksa menambahkan) dan cp /dev/null logmemotongnya?
Mark Plotnick

1
Apa pesan kesalahan yang Anda dapatkan? Perilaku apa yang Anda lihat? "Tidak mengarahkan outputnya ke file log lagi" tidak terlalu spesifik. Juga, cat "" > logbukan catperintah yang valid karena tidak ada file yang dipanggil "".
Mikel

Jawaban:


13

Bentuk lain dari masalah ini terjadi dengan aplikasi yang berjalan lama yang log-nya diputar secara berkala. Sekalipun Anda memindahkan log asli (mis., mv log.txt log.1) Dan segera menggantinya dengan file dengan nama yang sama sebelum terjadi pencatatan yang sebenarnya, jika proses tersebut menahan file tersebut tetap terbuka, ia akan berakhir menulis log.1(karena itu mungkin masih berupa inode terbuka) atau tidak sama sekali.

Cara umum untuk menangani hal ini (logger sistem itu sendiri bekerja dengan cara ini) adalah dengan mengimplementasikan pengendali sinyal dalam proses yang akan menutup dan membuka kembali log-nya. Kemudian, kapan pun Anda ingin memindahkan atau menghapus (dengan menghapus) log, kirim sinyal itu ke proses segera sesudahnya.

Berikut ini adalah demonstrasi sederhana untuk bash - maafkan keterampilan shell kasar saya (tetapi jika Anda akan mengedit ini untuk praktik terbaik, dll., Pastikan Anda memahami fungsi pertama dan menguji revisi Anda sebelum Anda mengedit):

#!/bin/bash

trap sighandler INT

function sighandler () {
    touch log.txt
    exec &> log.txt
}

echo $BASHPID
exec &> log.txt

count=0;
while [ $count -lt 60 ]; do
    echo "$BASHPID Count is now $count"
    sleep 2
    ((count++))
done          

Mulai ini dengan membentuk latar belakang:

> ./test.sh &
12356

Perhatikan itu melaporkan PID-nya ke terminal dan kemudian mulai masuk ke log.txt. Anda sekarang memiliki 2 menit untuk bermain-main. Tunggu beberapa detik dan coba:

> mv log.txt log.1 && kill -s 2 12356

Biasa saja kill -2 12356 dapat bekerja untuk Anda di sini juga. Sinyal 2 adalah SIGINT (itu juga yang dilakukan oleh Ctrl-C, jadi Anda bisa mencoba ini di latar depan dan memindahkan atau menghapus file log dari terminal lain), yang trapseharusnya dijebak. Untuk memeriksa;

> cat log.1
12356 Count is now 0
12356 Count is now 1
12356 Count is now 2
12356 Count is now 3
12356 Count is now 4
12356 Count is now 5
12356 Count is now 6
12356 Count is now 7
12356 Count is now 8
12356 Count is now 9
12356 Count is now 10
12356 Count is now 11
12356 Count is now 12
12356 Count is now 13
12356 Count is now 14

Sekarang mari kita lihat apakah masih menulis ke log.txtmeskipun kita memindahkannya:

> cat log.txt
12356 Count is now 15
12356 Count is now 16
12356 Count is now 17
12356 Count is now 18
12356 Count is now 19
12356 Count is now 20
12356 Count is now 21

Perhatikan itu terus berjalan tepat di tempat yang ditinggalkannya. Jika Anda tidak ingin menyimpan catatan, cukup hapus log dengan menghapusnya

> rm -f log.txt && kill -s 2 12356

Memeriksa:

> cat log.txt
12356 Count is now 29
12356 Count is now 30
12356 Count is now 31
12356 Count is now 32
12356 Count is now 33
12356 Count is now 34
12356 Count is now 35
12356 Count is now 36

Masih berjalan.

Sayangnya, Anda tidak dapat melakukan ini dalam skrip shell untuk subproses yang dieksekusi, karena jika berada di latar depan, penangan sinyal bash sendiri (trap ditangguhkan, dan jika Anda bercabang di latar belakang, Anda tidak dapat menugaskan kembali keluaran. Yaitu, ini adalah sesuatu yang harus Anda terapkan dalam aplikasi Anda.

Namun...

Jika Anda tidak dapat memodifikasi aplikasi (misalnya, karena Anda tidak menulisnya), saya memiliki utilitas CLI yang dapat Anda gunakan sebagai perantara. Anda juga bisa mengimplementasikan versi sederhana ini dalam skrip yang berfungsi sebagai pipa ke log:

#!/bin/bash

trap sighandler INT

function sighandler () {
    touch log.txt
    exec 1> log.txt
}

echo "$0 $BASHPID"
exec 1> log.txt

count=0;
while read; do
    echo $REPLY
done  

Sebut ini pipetrap.sh. Sekarang kita membutuhkan program terpisah untuk mengujinya, meniru aplikasi yang ingin Anda masuki:

#!/bin/bash

count=0
while [ $count -lt 60 ]; do
    echo "$BASHPID Count is now $count"
    sleep 2
    ((count++))
done           

Itu akan menjadi test.sh:

> (./test.sh | ./pipetrap.sh) &
./pipetrap.sh 15859

Ini adalah dua proses terpisah dengan PID terpisah. Untuk menghapus test.shkeluaran, yang sedang disalurkan melalui pipetrap.sh:

> rm -f log.txt && kill -s 2 15859

Memeriksa:

>cat log.txt
15858 Count is now 6
15858 Count is now 7
15858 Count is now 8

15858,, test.shmasih berjalan dan hasilnya sedang dicatat. Dalam hal ini, tidak diperlukan modifikasi pada aplikasi.


Terima kasih atas penjelasannya. Namun dalam kasus saya, saya tidak dapat memodifikasi aplikasi untuk mengimplementasikan solusi Anda.
bangnab

2
Jika Anda tidak dapat menerapkan penangan sinyal di aplikasi Anda (karena Anda tidak dapat memodifikasinya periode), Anda dapat menggunakan teknik ini untuk menyalurkan log melalui perangkap sinyal - lihat hal-hal setelah "Namun ..."
goldilocks

Ok, saya akan mencobanya dan memberi tahu Anda bagaimana hasilnya.
bangnab

Saya akhirnya memiliki aplikasi CLI yang ditulis dalam C untuk ini (maaf butuh waktu lebih lama dari yang dimaksudkan): cognitivedissonance.ca/cogware/pipelog
goldilocks

6

TL; DR

Buka file log Anda dalam mode tambahkan :

cmd >> log

Kemudian, Anda dapat memotongnya dengan aman dengan:

: > log

Detail

Dengan shell seperti Bourne, ada 3 cara utama file dapat dibuka untuk ditulis. Di tulis saja ( >), baca + tulis ( <>) atau tambahkan (dan tulis saja,>> mode ).

Pada dua yang pertama, kernel mengingat posisi Anda saat ini (maksud Anda, deskripsi file yang terbuka , dibagikan oleh semua deskriptor file yang telah menggandakan atau mewarisi itu dengan mem-forking dari yang Anda buka file) berada di dalam mengajukan.

Saat kamu melakukan:

cmd > log

logterbuka dalam mode tulis saja oleh shell untuk stdout cmd.

cmd(proses awalnya dihasilkan oleh shell dan semua anak yang mungkin) saat menulis ke stdout mereka, menulis pada posisi kursor saat ini yang dipegang oleh deskripsi file terbuka yang mereka bagi pada file itu.

Misalnya, jika cmdpada awalnya menulis zzz, posisi byte akan diimbangi 4 ke dalam file, dan waktu berikutnya cmdatau anak-anaknya menulis ke file, di situlah data akan ditulis terlepas dari apakah file telah tumbuh atau menyusut dalam interval .

Jika file telah menyusut, misalnya jika telah terpotong dengan a

: > log

dan cmdmenulis xx, itu xxakan ditulis di offset 4, dan 3 karakter pertama akan digantikan oleh karakter NUL.

$ exec 3> log # open file on fd 3.
$ printf zzz >&3
$ od -c log
0000000   z   z   z
0000003
$ printf aaaa >> log # other open file description -> different cursor
$ od -c log
0000000   z   z   z   a   a   a   a
0000007
$ printf bb >&3 # still write at the original position
$ od -c log
0000000   z   z   z   b   b   a   a
0000007
$ : > log
$ wc log
0 0 0 log
$ printf x >&3
$ od -c log
0000000  \0  \0  \0  \0  \0   x
0000006

Itu berarti Anda tidak dapat memotong file yang telah terbuka dalam mode tulis-saja (dan itu sama untuk baca + tulis ) seolah-olah Anda melakukannya, proses yang memiliki deskriptor file terbuka pada file, akan meninggalkan karakter NUL di awal file (itu, kecuali pada OS / X, biasanya tidak mengambil ruang pada disk sekalipun, mereka menjadi file jarang).

Alih-alih (dan Anda akan melihat sebagian besar aplikasi melakukannya ketika mereka menulis untuk mencatat file), Anda harus membuka file dalam mode append :

cmd >> log

atau

: > log && cmd >> log

jika Anda ingin memulai pada file kosong.

Dalam mode tambahkan, semua penulisan dilakukan di akhir file, terlepas dari di mana penulisan terakhir:

$ exec 4>> log
$ printf aa >&4
$ printf x >> log
$ printf bb >&4
$ od -c log
0000000   a   a   x   b   b
0000005
$ : > log
$ printf cc >&4
$ od -c log
0000000   c   c
0000002

Itu juga lebih aman seolah-olah dua proses membuka (dengan cara itu) file secara tidak sengaja (seperti misalnya jika Anda sudah memulai dua contoh dari daemon yang sama), hasilnya tidak akan saling menimpa.

Pada versi Linux terbaru, Anda dapat memeriksa posisi saat ini dan apakah deskriptor file telah terbuka dalam mode penambahan dengan melihat /proc/<pid>/fdinfo/<fd>:

$ cat /proc/self/fdinfo/4
pos:        2
flags:      0102001

Atau dengan:

$ lsof +f G -p "$$" -ad 4
COMMAND  PID USER   FD   TYPE  FILE-FLAG DEVICE SIZE/OFF     NODE NAME
zsh     4870 root    4w   REG 0x8401;0x0 252,18        2 59431479 /home/chazelas/log
~# lsof +f g -p "$$" -ad 4
COMMAND  PID USER   FD   TYPE FILE-FLAG DEVICE SIZE/OFF     NODE NAME
zsh     4870 root    4w   REG   W,AP,LG 252,18        2 59431479 /home/chazelas/log

Mereka bendera sesuai dengan O ..._ bendera diteruskan ke opensystem call.

$ gcc -E - <<< $'#include <fcntl.h>\nO_APPEND O_WRONLY' | tail -n1
02000 01

( O_APPENDadalah 0x400 atau oktal 02000)

Jadi shell >>membuka file dengan O_WRONLY|O_APPEND(dan 0.100.000 di sini adalah O_LARGEFILE yang tidak relevan dengan pertanyaan ini) sementara >adalah O_WRONLYhanya (dan <>ini O_RDWRhanya).

Jika Anda melakukan:

sudo lsof -nP +f g | grep ,AP

untuk mencari file yang terbuka O_APPEND, Anda akan menemukan sebagian besar file log saat ini terbuka untuk ditulis di sistem Anda.


Mengapa Anda menggunakan :(titik dua) di : > ?
mvorisek

1
@Mvorisek, itu untuk mengarahkan output dari perintah yang tidak menghasilkan output: :. Tanpa perintah, perilaku bervariasi antara shell.
Stéphane Chazelas

1

Jika saya memahami dengan benar, teesepertinya pendekatan yang masuk akal:

$ ./myapp-that-echoes-the-date-every-second | tee log > /dev/null &
[1] 20519
$ head log
Thu Apr  3 11:29:34 EDT 2014
Thu Apr  3 11:29:35 EDT 2014
Thu Apr  3 11:29:36 EDT 2014
$ > log
$ head log
Thu Apr  3 11:29:40 EDT 2014
Thu Apr  3 11:29:41 EDT 2014
Thu Apr  3 11:29:42 EDT 2014

1

Sebagai solusi cepat, Anda dapat menggunakan log dengan rotasi (rotasi harian misalnya):

date=`date +%Y%m%d`
LOGFILE=/home/log$date.log

dan redirect logging ke sana ./my_app >> log$date.log


Saya ingin dapat memutar sesuai permintaan. Ini sebenarnya adalah log yang dihasilkan selama tes otomatis dan saya ingin menghapusnya sebelum menjalankan tes.
bangnab

0

Ini adalah masalah yang telah lama diselesaikan dengan syslog (dalam semua variannya) tetapi ada dua alat yang akan memecahkan masalah khusus Anda dengan upaya minimal.

Solusi pertama, lebih portabel tetapi kurang fleksibel adalah logger (harus dimiliki oleh setiap toolbox administrator). Ini adalah utilitas sederhana yang menyalin input standar ke syslog. (melewati tanggung jawab, dan membuat rotasi file masalah logrotate dan syslog)

Solusi kedua, lebih elegan tetapi kurang portabel adalah syslog-ng yang selain menerima pesan log dari soket syslog standar dapat menjalankan program yang hasilnya disaring melalui logger. (Saya belum menggunakan fitur ini, tetapi terlihat sempurna untuk apa yang ingin Anda lakukan.)

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.