หางอ่านไฟล์ทั้งหมดหรือไม่?


113

หากฉันต้องการtailไฟล์ข้อความ 25 GB tailคำสั่งจะอ่านไฟล์ทั้งหมดหรือไม่

เนื่องจากไฟล์อาจกระจายอยู่ในดิสก์ฉันคิดว่ามันมี แต่ฉันไม่เข้าใจ internals ดี

คำตอบ:


119

ไม่มีtailไม่ได้อ่านไฟล์ทั้งหมดก็พยายามที่จะสิ้นสุดแล้วอ่านบล็อกถอยหลังจนถึงจำนวนที่คาดหวังของสายได้รับมาถึงแล้วก็จะแสดงเส้นในทิศทางที่ถูกต้องจนกว่าจะสิ้นสุดของแฟ้มและอาจจะยังคงตรวจสอบ ไฟล์ถ้าใช้-fตัวเลือก

อย่างไรก็ตามโปรดทราบว่าtailไม่มีทางเลือกนอกจากอ่านข้อมูลทั้งหมดหากมีการป้อนข้อมูลที่ไม่สามารถค้นหาได้เช่นเมื่ออ่านจากไปป์

ในทำนองเดียวกันเมื่อถูกขอให้ค้นหาบรรทัดที่เริ่มต้นจากจุดเริ่มต้นของไฟล์ด้วยการใช้tail -n +linenumberไวยากรณ์หรือtail +linenumberตัวเลือกที่ไม่ได้มาตรฐานเมื่อได้รับการสนับสนุนtailอย่างชัดเจนอ่านไฟล์ทั้งหมด (เว้นแต่ถูกขัดจังหวะ)


14
ประณามเร็วเกินไป :-) นี่คือรหัสที่มาเกี่ยวข้อง พิมพ์บรรทัด N_LINES สุดท้ายจากท้ายไฟล์ FD ย้อนกลับไปที่ไฟล์อ่านไบต์ 'BUFSIZ' (ยกเว้นอาจเป็นครั้งแรก) จนกว่าเราจะเริ่มต้นไฟล์หรืออ่าน NUMBER บรรทัดใหม่
แพทริค

1
นอกจากนี้tail +nจะอ่านไฟล์ทั้งหมด - ก่อนอื่นเพื่อค้นหาจำนวนบรรทัดใหม่ที่ต้องการจากนั้นจึงส่งออกส่วนที่เหลือ
เอสเอฟ

@SF แน่นอนตอบปรับปรุง
jlliagre

4
โปรดทราบว่าtailการใช้งานไม่ทั้งหมดทำหรือทำอย่างถูกต้อง ตัวอย่างเช่น busybox 1.21.1 tailนั้นใช้งานไม่ได้ นอกจากนี้โปรดทราบว่าพฤติกรรมจะแตกต่างกันเมื่อtailing stdin และที่ stdin เป็นไฟล์ปกติและตำแหน่งเริ่มต้นในไฟล์ไม่ได้อยู่ที่จุดเริ่มต้นเมื่อtailมีการเรียกใช้ (เช่นใน{ cat > /dev/null; tail; } < file)
Stéphane Chazelas

4
@StephaneChazelas * ระวัง - โลกของกรณีขอบแปลกกลายเป็นเรื่องปกติ (แม้ว่าอินพุตที่หาได้และไม่สามารถค้นหาได้เป็นจุดที่ถูกต้องแน่นอน)
CVn

69

คุณจะได้เห็นวิธีการtailทำงานของตัวเอง อย่างที่คุณสามารถทำได้สำหรับหนึ่งในไฟล์ของฉันreadทำสามครั้งและอ่านทั้งหมด 10K ไบต์โดยรวม:

strace 2>&1  tail ./huge-file >/dev/null  | grep -e "read" -e "lseek" -e "open" -e "close"
open("./huge-file", O_RDONLY)           = 3
lseek(3, 0, SEEK_CUR)                   = 0
lseek(3, 0, SEEK_END)                   = 80552644
lseek(3, 80551936, SEEK_SET)            = 80551936
read(3, ""..., 708) = 708
lseek(3, 80543744, SEEK_SET)            = 80543744
read(3, ""..., 8192) = 8192
read(3, ""..., 708) = 708
close(3)                                = 0

ฉันไม่เห็นว่าสิ่งนี้ตอบคำถามได้อย่างไร คุณช่วยอธิบายสิ่งที่เกิดขึ้นที่นี่ได้ไหม?
เลนซามูเอลแมคลีนผู้อาวุโส

10
straceแสดงสิ่งที่เรียกระบบtailทำเมื่อทำงาน introdaction บางอย่างเกี่ยวกับระบบการเรียกคุณสามารถอ่านได้ที่นี่en.wikipedia.org/wiki/System_call สั้น ๆ - เปิด - เปิดไฟล์และส่งคืนหมายเลขอ้างอิง (3 ในตัวอย่างนี้) lseekตำแหน่งที่คุณกำลังจะอ่านและreadเพิ่งอ่านและตามที่คุณเห็นว่ามันส่งคืนจำนวนไบต์ที่อ่านจำนวนหนึ่ง
Sergei Kurenkov

2
ดังนั้นการวิเคราะห์ระบบการโทรบางครั้งคุณสามารถเข้าใจวิธีการทำงานของโปรแกรม
Sergei Kurenkov

26

เนื่องจากไฟล์อาจกระจายอยู่ในดิสก์ฉันคิดว่ามันต้อง [อ่านไฟล์ตามลำดับ] แต่ฉันไม่เข้าใจ internals ดังกล่าวดี

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

คำตอบนั้นง่าย: หางไม่รู้ กระบวนการระดับผู้ใช้จะเห็นไฟล์เป็นสตรีมต่อเนื่องดังนั้นทุกคนtailสามารถรู้ได้ว่าเป็นการออฟเซ็ตจากจุดเริ่มต้นของไฟล์ แต่ในระบบไฟล์ไฟล์ "inode" (รายการไดเรกทอรี) จะเชื่อมโยงกับรายการตัวเลขที่แสดงถึงตำแหน่งทางกายภาพของบล็อกข้อมูลของไฟล์ เมื่อคุณอ่านจากไฟล์เคอร์เนล / ไดรเวอร์อุปกรณ์จะเป็นตัวกำหนดว่าคุณต้องการส่วนใดทำงานอย่างไรตำแหน่งที่ตั้งของดิสก์และดึงข้อมูลให้คุณ

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


2

หากheadหรือtail ดูเหมือนว่ากำลังอ่านไฟล์ทั้งหมดสาเหตุที่เป็นไปได้คือไฟล์นั้นมีอักขระขึ้นบรรทัดใหม่น้อยหรือไม่มีเลย ฉันสะดุดมากขึ้นเมื่อไม่กี่เดือนที่ผ่านมาด้วยหยดขนาดใหญ่ (กิกะไบต์) JSON ที่ต่อเนื่องกันโดยไม่มีช่องว่างใด ๆ เลยแม้แต่ในสาย

หากคุณมีหัว / หาง GNU คุณสามารถใช้-c Nเพื่อพิมพ์ N ไบต์แรก / สุดท้ายแทนเส้นแต่น่าเสียดายที่นี่ไม่ใช่คุณสมบัติ POSIX


1

อย่างที่คุณเห็นในซอร์สโค้ดบรรทัด 525 คุณสามารถเห็นข้อคิดเห็นสำหรับการนำไปใช้งาน

 /* Print the last N_LINES lines from the end of file FD.
   Go backward through the file, reading 'BUFSIZ' bytes at a time (except
   probably the first), until we hit the start of the file or have
   read NUMBER newlines.
   START_POS is the starting position of the read pointer for the file
   associated with FD (may be nonzero).
   END_POS is the file offset of EOF (one larger than offset of last byte).
   Return true if successful.  */
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.