คุณจะเก็บเฉพาะไฟล์ n บรรทัดสุดท้ายของไฟล์บันทึกได้อย่างไร


18

สคริปต์ที่ฉันเขียนทำบางสิ่งและท้ายที่สุดจะผนวกบางบรรทัดเข้ากับล็อกไฟล์ของตัวเอง ฉันต้องการเก็บเฉพาะไฟล์ n บรรทัดสุดท้าย (พูด, 1,000 บรรทัด) ของล็อกไฟล์ ซึ่งสามารถทำได้ในตอนท้ายของสคริปต์ด้วยวิธีนี้:

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

แต่จะมีวิธีที่สะอาดและสง่างามกว่านี้ไหม? อาจสำเร็จด้วยคำสั่งเดียว?


logrotateเป็นทางออกที่สง่างาม
Ipor Sircer

1
ผมเคยคิดว่ามัน แต่การกำหนดค่า logrotate จะนานกว่าสคริปต์ของตัวเอง ...
dr01

หาก logrotate overkill คำตอบของคุณจะสวยหรูเท่าที่ควร ด้วย sed / awk คุณอาจทำได้ในหนึ่งบรรทัด แต่ไม่มีไฟล์ temp ภายในจึงอาจไม่มีประสิทธิภาพมากกว่าและอาจอ่านได้น้อยกว่า
kba ย่อมาจาก Monica

คำตอบ:


28

เป็นไปได้เช่นนี้ แต่อย่างที่คนอื่นพูดตัวเลือกที่ปลอดภัยที่สุดคือการสร้างไฟล์ใหม่จากนั้นย้ายไฟล์ดังกล่าวเพื่อเขียนทับต้นฉบับ

วิธีการด้านล่างโหลดบรรทัดลงใน BASH ดังนั้นขึ้นอยู่กับจำนวนบรรทัดtailที่จะส่งผลต่อการใช้หน่วยความจำของเชลล์โลคัลเพื่อเก็บเนื้อหาของบันทึกการทำงาน

ด้านล่างนี้ยังลบบรรทัดว่างที่ควรอยู่ในตอนท้ายของไฟล์บันทึก (เนื่องจากพฤติกรรมของการประเมินผล BASH "$(tail -1000 test.log)") ดังนั้นจึงไม่ให้การตัดทอนที่แม่นยำอย่างแท้จริง 100% ในทุกสถานการณ์ แต่ขึ้นอยู่กับสถานการณ์ของคุณอาจเพียงพอ

$ wc -l myscript.log
475494 myscript.log

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

$ wc -l myscript.log
1000 myscript.log

ฉลาด. ฉันทำเครื่องหมายว่านี่เป็นคำตอบที่ยอมรับเนื่องจากไม่จำเป็นต้องติดตั้งเครื่องมือเพิ่มเติม ฉันหวังว่าฉันจะยอมรับทั้งคำตอบของคุณและ @ John1024
dr01

การโทรของคุณ ฉันยกระดับโซลูชันฟองน้ำเนื่องจากฉันไม่รู้เกี่ยวกับมันและรับประกันว่าจะไม่ยุ่งกับบรรทัดบันทึกที่ว่างเปล่า วิธีนี้มีความเป็นไปได้ที่จะทำเช่นนั้นขึ้นอยู่กับเนื้อหาของไฟล์บันทึก
parkamark

วิธีนี้มีสภาพการแข่งขัน หากคุณโชคไม่ดีการเปลี่ยนเส้นทาง - เป็น - ไฟล์จะเกิดขึ้นก่อนที่จะอ่าน - จาก - ไฟล์และคุณจะจบด้วยไฟล์เปล่า
Coroos

21

ยูทิลิตี้spongeถูกออกแบบมาสำหรับกรณีนี้ หากคุณติดตั้งแล้วคุณสามารถเขียนสองบรรทัดได้:

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

โดยปกติแล้วการอ่านจากไฟล์ในเวลาเดียวกันกับที่คุณเขียนไว้นั้นไม่น่าเชื่อถือ spongeแก้ปัญหานี้โดยไม่ได้เขียนmyscript.logจนกระทั่งหลังจากtailอ่านเสร็จแล้วและยุติท่อ

ติดตั้ง

ในการติดตั้งspongeบนระบบที่เหมือน Debian:

apt-get install moreutils

หากต้องการติดตั้งspongeบนระบบ RHEL / CentOS ให้เพิ่ม EPEL repo จากนั้นทำ:

yum install moreutils

เอกสาร

จากman sponge:

spongeอ่านอินพุตมาตรฐานและเขียนลงในไฟล์ที่ระบุ แตกต่างจากการเปลี่ยนเส้นทางของเชลล์spongeดูดซับข้อมูลทั้งหมดก่อนที่จะเขียนไฟล์เอาต์พุต สิ่งนี้อนุญาตให้สร้างท่อที่อ่านและเขียนไปยังไฟล์เดียวกัน


2
+1 spongeขอบคุณผมไม่ทราบ มีประโยชน์มากสำหรับทุกคนที่ได้เรียนรู้วิธีที่ยากที่คุณไม่สามารถทำsort importantfile.txt > importantfile.txt:)
dr01

4

แน่นอน "tail + mv" ดีกว่ามาก! แต่สำหรับ gnu sed เราสามารถลองได้

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

3

สำหรับบันทึกedคุณสามารถทำอะไรบางอย่างเช่น

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

นี้จะเปิดinfileและreads ในการส่งออกของtail -n 1000 infile(เช่นมันแทรกเอาท์พุทก่อนที่บรรทัดที่ 1) และจากนั้นลบจากสิ่งที่เป็นบรรทัดแรกที่ 1 ถึงจุดสิ้นสุดของไฟล์ แทนที่,pด้วยwเพื่อแก้ไขไฟล์ในตำแหน่ง
โปรดทราบว่าedโซลูชันไม่เหมาะสำหรับไฟล์ขนาดใหญ่


0

สิ่งที่คุณสามารถทำได้ในสคริปต์ของคุณคือการใช้ตรรกะของการหมุนเวียนบันทึก ทำการบันทึกทั้งหมดผ่านฟังก์ชั่น:

log()
{
   ...
}

ฟังก์ชั่นนี้ประการแรกทำสิ่งที่ชอบ:

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

จากนั้นจะตรวจสอบขนาดของไฟล์หรือตัดสินใจว่าไฟล์นั้นต้องการการหมุน ณ จุดที่ไฟล์logfile.1ถ้ามันมีอยู่แล้วจะถูกลบออกไฟล์logfile.0ถ้ามันมีอยู่แล้วจะเปลี่ยนชื่อlogfile.1และมีการเปลี่ยนชื่อเป็นlogfilelogfile.0

การตัดสินใจว่าจะหมุนเวียนอาจขึ้นอยู่กับตัวนับที่เก็บรักษาไว้ในสคริปต์ เมื่อถึง 1,000 มันจะถูกรีเซ็ตเป็นศูนย์

หากต้องการตัดแต่งอย่างเคร่งครัดถึง 1,000 บรรทัดสคริปต์จะสามารถนับจำนวนบรรทัดในล็อกไฟล์เมื่อเริ่มต้นและเริ่มต้นตัวนับตามนั้น (หรือถ้าจำนวนนั้นตรงกับหรือเกินกว่า 1,000 ให้หมุนทันที)

หรือคุณอาจได้ขนาดเช่นกับwc -c logfileและหมุนตามขนาดที่กำหนด วิธีนี้ไม่ต้องสแกนไฟล์เพื่อกำหนดเงื่อนไข


0

ฉันไม่ได้ใช้แทนการmvที่cpคำสั่งเพื่อให้บรรลุมันว่าคุณมีความสามารถที่จะมีบาง logfiles เหมาะสมในสถานที่ที่มีการเรียกใช้ซอฟแวร์ อาจอยู่ใน dir ของผู้ใช้ที่แตกต่างกันหรือใน dir ของแอพและมีบันทึกทั้งหมดในที่เดียวว่าเป็นลิงก์ หากคุณใช้mvคำสั่งคุณจะสูญเสียการเชื่อมโยงอย่างหนัก หากคุณใช้cpคำสั่งแทนคุณจะเก็บฮาร์ดลิงก์นี้ไว้

รหัสของฉันเป็นสิ่งที่ชอบ:

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   

ดังนั้นหากไฟล์อยู่ในระบบไฟล์เดียวกันคุณอาจให้สิทธิ์แตกต่างกันบางอย่างแก่ผู้ใช้และใน${LOGFILE_DIR}ระยะเวลาที่คุณปรับเปลี่ยนเช่นฉัน

หากเป็นmvคำสั่งคุณจะสูญเสียฮาร์ดลิงก์ระหว่างไฟล์ดังนั้นไฟล์ที่สองของคุณจะไม่เชื่อมต่อกับไฟล์แรก - อาจวางไว้ที่อื่น

หากที่อื่นคุณไม่อนุญาตให้ใครบางคนลบไฟล์บันทึกของคุณอยู่ด้วยกันและควบคุมได้ดีผ่านสคริปต์ของคุณเอง

logrotateอาจจะดีกว่า แต่ฉันมีความสุขกับการแก้ปัญหานี้

อย่าถูกรบกวนโดย "" แต่ในกรณีของฉันมีไฟล์บางตัวที่มีช่องว่างและตัวอักษรพิเศษอื่น ๆ อยู่และถ้าฉันไม่ทำ "" หรือ {} ล็อตทั้งหมดก็ใช้งานไม่ได้

ตัวอย่างเช่นมี Dir ที่ไฟล์เก่าได้รับการซิปแบบอัตโนมัติOLDFILE.zipและทุกอย่างที่ได้รับการซิปนั้นมีการระบุไว้ใน File .zip_logดังนั้น.zip_logใน Dir นี้ก็เช่นกัน แต่ในที่LOGFILE_DIRฉันมี:

ln .zip_log "${LOGFILE_DIR}/USER_ZIP_log"

ไฟล์ที่เท่ากันเนื่องจากเป็นฮาร์ดลิงก์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.