วิธียกเลิกคำสั่ง Linux โดยไม่ทำลายแอปพลิเคชันที่ได้รับ


19

ฉันมีสคริปต์ทุบตีที่ทำงานตราบใดที่เครื่อง Linux เปิดอยู่ ฉันเริ่มมันดังแสดงด้านล่าง:

( /mnt/apps/start.sh 2>&1 | tee /tmp/nginx/debug_log.log ) &

หลังจาก lauches ฉันสามารถดูคำสั่ง tee ในเอาต์พุตpsของฉันได้ดังแสดงด้านล่าง

$ ps | grep tee
  418 root       0:02 tee /tmp/nginx/debug_log.log
3557 root       0:00 grep tee

ฉันมีฟังก์ชั่นที่ตรวจสอบขนาดของบันทึกที่teeสร้างและฆ่าคำสั่งteeเมื่อบันทึกถึงขนาดที่แน่นอน:

monitor_debug_log_size() {
                ## Monitor the file size of the debug log to make sure it does not get too big
                while true; do
                                cecho r "CHECKING DEBUG LOG SIZE... "
                                debugLogSizeBytes=$(stat -c%s "/tmp/nginx/debug_log.log")
                                cecho r "DEBUG LOG SIZE: $debugLogSizeBytes"
                                if [ $((debugLogSizeBytes)) -gt 100000 ]; then
                                                cecho r "DEBUG LOG HAS GROWN TO LARGE... "
                                                sleep 3
                                                #rm -rf /tmp/nginx/debug_log.log 1>/dev/null 2>/dev/null
                                                kill -9 `pgrep -f tee`
                                fi
                                sleep 30
                done
}

ด้วยความประหลาดใจของฉันการฆ่าคำสั่งteeก็ฆ่าโดย start.sh เช่นกัน ทำไมนี้ ฉันจะจบคำสั่งteeได้อย่างไร แต่ให้ start.sh ยังคงทำงานต่อไป? ขอบคุณ

คำตอบ:


34

เมื่อ teeยกเลิกคำสั่งการป้อนจะยังคงทำงานจนกว่าจะพยายามเขียนเอาต์พุตเพิ่มเติม จากนั้นจะได้รับ SIGPIPE (13 ระบบส่วนใหญ่) สำหรับการพยายามเขียนลงในไพพ์ที่ไม่มีผู้อ่าน

หากคุณแก้ไขสคริปต์ของคุณเพื่อดักจับ SIGPIPE และดำเนินการบางอย่างที่เหมาะสม (เช่นหยุดเขียนเอาต์พุต) จากนั้นคุณควรจะสามารถดำเนินการต่อได้หลังจากทีออฟสิ้นสุดลง


ยังดีกว่าแทนที่จะฆ่าtee เลยใช้logrotateกับcopytruncateตัวเลือกเพื่อความเรียบง่าย

อ้างถึงlogrotate(8):

copytruncate

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


9
คุณต้องการที่จะใช้tee -aสำหรับteeเปิดไฟล์ในโหมดต่อท้ายมิฉะนั้นทีออฟจะทำการเขียนลงในไฟล์ที่ออฟเซ็ตเดียวกันหลังจากคุณตัดทอน (และในระบบที่ไม่รองรับไฟล์ที่กระจัดกระจายเช่นใน macOS ที่จะ จัดสรรส่วนของไฟล์ที่นำไปสู่ตำแหน่งนั้นโดยใช้พื้นที่ดิสก์เป็นสองเท่า)
Stéphane Chazelas

4
ตัวเลือกอื่นคือไปป์เพื่อlogger -sให้ syslog ดูแลการบันทึก ( -sเพื่อพิมพ์บน stderr)
Stéphane Chazelas

1
+1 logrotateสำหรับ โปรแกรมที่ยอดเยี่ยม
Dmitry Kudriavtsev

2
หรือบนระบบที่ใช้ systemd และ journald, systemd-cat แทนตัวบันทึก จากนั้นคุณจะได้รับการกรองเอาท์พุทและการหมุนฟรีมากมาย
Zan Lynx

3

อธิบาย "ทำไม"

กล่าวโดยย่อ: หากการเขียนที่ล้มเหลวไม่ทำให้โปรแกรมออก (โดยค่าเริ่มต้น) เราจะมีระเบียบ ลองพิจารณาfind . | head -n 10- คุณไม่ต้องการfindทำงานต่อไปสแกนส่วนที่เหลือของฮาร์ดไดรฟ์หลังจากนั้นheadได้รับ 10 บรรทัดที่จำเป็นและดำเนินการต่อไป

การทำให้ดีขึ้น: หมุนตัวบันทึกของคุณ

ลองพิจารณาสิ่งต่อไปนี้ซึ่งไม่ได้ใช้teeเลยเป็นตัวอย่างที่แสดงให้เห็น:

#!/usr/bin/env bash

file=${1:-debug.log}                     # filename as 1st argument
max_size=${2:-100000}                    # max size as 2nd argument
size=$(stat --format=%s -- "$file") || exit  # Use GNU stat to retrieve size
exec >>"$file"                           # Open file for append

while IFS= read -r line; do              # read a line from stdin
  size=$(( size + ${#line} + 1 ))        # add line's length + 1 to our counter
  if (( size > max_size )); then         # and if it exceeds our maximum...
    mv -- "$file" "$file.old"            # ...rename the file away...
    exec >"$file"                        # ...and reopen a new file as stdout
    size=0                               # ...resetting our size counter
  fi
  printf '%s\n' "$line"                  # regardless, append to our current stdout
done

ถ้าเรียกใช้เป็น:

/mnt/apps/start.sh 2>&1 | above-script /tmp/nginx/debug_log

... สิ่งนี้จะเริ่มต้นด้วยการต่อท้าย/tmp/nginx/debug_logเปลี่ยนชื่อไฟล์เป็น/tmp/nginx/debug_log.oldเมื่อมีเนื้อหามากกว่า 100KB เนื่องจากคนตัดไม้กำลังทำการหมุนจึงไม่มีการแบ่งท่อไม่มีข้อผิดพลาดและไม่มีหน้าต่างข้อมูลสูญหายเมื่อมีการหมุนเกิดขึ้น - ทุกบรรทัดจะถูกเขียนลงในไฟล์เดียวหรืออีกอันหนึ่ง

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

  • svlogdเซอร์วิสตัวบันทึกจากชุด Runit
  • s6-logทางเลือกที่ได้รับการดูแลรักษาอย่างแข็งขันจากชุด skanet
  • multilog จาก DJB Daemontools ปู่ของตระกูลการควบคุมกระบวนการและเครื่องมือการตรวจสอบ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.