คำสั่งให้แสดงสองสามบรรทัดแรกและสองสามบรรทัดสุดท้ายของไฟล์


23

ฉันมีไฟล์ที่มีหลายแถวและแต่ละแถวมีการประทับเวลาตอนเริ่มต้นเช่น

[Thread-3] (21/09/12 06:17:38:672) logged message from code.....

ดังนั้นฉันมักตรวจสอบ 2 สิ่งจากไฟล์บันทึกนี้

  1. สองสามแถวแรกที่มีเงื่อนไขโกลบอลและเวลาเริ่มต้นจะได้รับด้วย
  2. ไม่กี่แถวสุดท้ายที่มีสถานะการออกพร้อมกับข้อมูลอื่น ๆ

มีคำสั่งเดียวที่มีประโยชน์อย่างรวดเร็วที่สามารถให้ฉันแสดงบรรทัดแรกและสองสามบรรทัดของไฟล์ได้หรือไม่?


2
เงื่อนไขระดับโลกคืออะไรและไม่ได้head and tailผลสำหรับคุณ
เดซี่

นั่นคือส่วนหนึ่งของไฟล์บันทึกของฉัน ฉันพยายามทำอย่างละเอียด คุณสามารถเพิกเฉยได้
mtk

ทางออกของคุณดูดีสำหรับฉัน ถ้าคุณต้องการความสะดวกเพิ่มขึ้นให้มันเป็นฟังก์ชั่นของเชลล์ (แม้แต่นามแฝงก็อาจทำได้)
vonbrand

@vonbrand ปัญหาคือฉันไม่รู้N
Bernhard

@ เบอร์นาร์ดฉันไม่sed(1)ชำนาญ แต่มีวิธีการเก็บข้อมูลเพื่อใช้กับมันในภายหลัง บางทีมันอาจจะดูดี OTOH ฉันอาจใช้สคริปต์ Perl (หรืออะไรก็ได้) เพื่อทำถ้าใช้บ่อยเพราะฉันคุ้นเคยกับมันมากกว่า
vonbrand

คำตอบ:


12

คุณสามารถใช้sedหรือawkสร้างด้วยคำสั่งเดียว อย่างไรก็ตามคุณจะสูญเสียความเร็วสาเหตุsedและawkจะต้องเรียกใช้ผ่านไฟล์ทั้งหมดต่อไป จากจุดความเร็วในมุมมองของมันมากดีกว่าที่จะทำให้การทำงานหรือทุกครั้งที่การรวมกันของ+tail headนี่เป็นข้อเสียของการไม่ทำงานหากอินพุตเป็นไพพ์อย่างไรก็ตามคุณสามารถใช้การทดแทน proccess ในกรณีที่เชลล์ของคุณรองรับมัน (ดูตัวอย่างด้านล่าง)

first_last () {
    head -n 10 -- "$1"
    tail -n 10 -- "$1"
}

และเพียงแค่เปิดมันเป็น

first_last "/path/to/file_to_process"

เพื่อดำเนินการทดแทนกระบวนการ (bash, zsh, ksh like shells เท่านั้น):

first_last <( command )

PS คุณสามารถเพิ่ม a grepเพื่อตรวจสอบว่ามี "เงื่อนไขทั่วโลก" ของคุณอยู่หรือไม่


-n 10ค่าเริ่มต้นคืออะไร
l0b0

@ l0b0 ใช่มันเป็นค่าเริ่มต้น -n 10ไม่จำเป็นที่นี่
เร่ง

20

@rush ถูกต้องเกี่ยวกับการใช้ head + tail ซึ่งมีประสิทธิภาพมากขึ้นสำหรับไฟล์ขนาดใหญ่ แต่สำหรับไฟล์ขนาดเล็ก (<20 บรรทัด) บางบรรทัดอาจถูกส่งออกสองครั้ง

{ head; tail;} < /path/to/file

จะมีประสิทธิภาพเท่าเทียมกัน แต่จะไม่มีปัญหาข้างต้น


ตรงกันข้ามกับวิธีการแก้ปัญหารัชนี่ไม่ทำงานในเปลือก POSIX
Marco

2
@Marco Huh? โครงสร้าง POSIX เท่านั้นที่ใช้ที่นี่ คุณเห็นอะไรผิดพลาด
Gilles 'หยุดชั่วร้าย'

2
@Gilles ฉันพลาดพื้นที่: {head; tail;} < fileทำงานใน zsh แต่ล้มเหลวใน sh { head; tail;} < fileทำงานได้เสมอ ขอโทษสำหรับเสียงรบกวน
มาร์โก

@Marco หากมีปัญหากับมันมันจะอยู่กับheadเชลล์ไม่ใช่ POSIX ต้องheadปล่อยเคอร์เซอร์ไว้ในไฟล์ที่ผ่านมา 10 บรรทัดสำหรับไฟล์ปกติ ปัญหาอาจเกิดขึ้นสำหรับheadการใช้งานที่ไม่ใช่ POSIX (หัว GNU รุ่นเก่าซึ่งเคยเป็นแบบไม่สอดคล้องในกรณีนั้น แต่เรากำลังพูดถึงหลายสิบปี) หรือถ้าไฟล์นั้นหาไม่ได้ (เช่นชื่อไพพ์หรือซ็อกเก็ต วิธีแก้ปัญหาอื่นจะมีปัญหาเดียวกัน)
Stéphane Chazelas

1
@FCTWsudo sh -c '{ head; tail;} < /path/to/file'
Stéphane Chazelas

9

{ head; tail; }วิธีการแก้ปัญหาจะไม่ทำงานในท่อ (หรือซ็อกเก็ตหรือไฟล์ที่ไม่ seekable อื่น ๆ ) เพราะheadสามารถใช้ข้อมูลที่มากเกินไปที่จะอ่านโดยบล็อกและไม่สามารถขอย้อนกลับไปในท่อที่อาจเกิดขึ้นออกจากเคอร์เซอร์ภายในแฟ้มเกินกว่าสิ่งที่tailมีความหมาย เลือก.

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

head_tail() {
  n=0
  while [ "$n" -lt "$1" ]; do
    IFS= read -r line || { printf %s "$line"; break; }
    printf '%s\n' "$line"
    n=$(($n + 1))
  done
  tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5

หรือนำไปใช้งานtailใน awk เช่น as:

head_tail() {
  awk -v h="$1" -v t="${2-$1}" '
    {l[NR%t]=$0}
    NR<=h
    END{
      n=NR-t+1
      if(n <= h) n = h+1
      for (;n<=NR;n++) print l[n%t]
    }'
}

ด้วยsed:

head_tail() {
  sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),\$!{N;b1" -e '}' -e 'N;D'
}

(แม้ว่าระวังว่าsedการใช้งานบางอย่างมีข้อ จำกัด ต่ำเกี่ยวกับขนาดของพื้นที่รูปแบบของพวกเขาดังนั้นจะล้มเหลวสำหรับค่าขนาดใหญ่ของจำนวนเส้นหาง)


4

การใช้การbashทดแทนกระบวนการคุณสามารถทำสิ่งต่อไปนี้:

make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null

โปรดทราบว่าบรรทัดนั้นไม่ได้รับประกันว่าจะเป็นไปตามลำดับ แต่สำหรับไฟล์ที่ยาวกว่าประมาณ 8kB พวกมันน่าจะเป็นมาก ทางลัด 8kB นี้เป็นขนาดปกติของบัฟเฟอร์การอ่านและเกี่ยวข้องกับเหตุผลที่ใช้| {head; tail;}ไม่ได้กับไฟล์ขนาดเล็ก

cat >/dev/nullเป็นสิ่งจำเป็นที่จะทำให้headท่อยังมีชีวิตอยู่ มิฉะนั้นteeจะหยุดก่อนและในขณะที่คุณจะได้รับผลลัพธ์tailมันจะมาจากที่ใดที่หนึ่งในช่วงกลางของการป้อนข้อมูลแทนที่จะจบ

สุดท้ายทำไม>/dev/nullแทนที่จะบอกว่าย้ายtailไปยังอีก|? ในกรณีต่อไปนี้:

make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2  # doesn't work

headstdout ถูกป้อนเข้าสู่ pipe tailแทนที่จะเป็น console ซึ่งไม่ใช่สิ่งที่เราต้องการเลย


เมื่อหัวหรือหางเขียนผลลัพธ์ที่ต้องการเสร็จพวกเขาจะปิด stdin และออก นั่นคือที่มาของ SIGPIPE โดยปกติสิ่งนี้เป็นสิ่งที่ดีพวกเขากำลังทิ้งส่วนที่เหลือของเอาต์พุตดังนั้นจึงไม่มีเหตุผลที่ด้านอื่น ๆ ของท่อจะใช้เวลาสร้างมันต่อไป
Derobert

อะไรทำให้คำสั่งมีแนวโน้มที่จะได้รับการรักษา? อาจเป็นไฟล์ขนาดใหญ่เพราะtailต้องใช้เวลานานกว่า แต่ฉันคาดว่า (และเห็น) มันล้มเหลวประมาณครึ่งเวลาสำหรับอินพุตสั้น
Gilles 'หยุดชั่วร้าย'

คุณจะได้รับ SIGPIPE ด้วยtee >(head) >(tail)เหตุผลเดียวกัน ( >(...)ซึ่งเป็นคุณลักษณะที่ ksh ได้รับการสนับสนุนจากทั้ง zsh และ bash ด้วย) ที่ใช้ไพพ์เช่นกัน คุณสามารถทำ... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)แต่คุณจะยังคงเห็นบางท่อเสียteeข้อความผิดพลาดจาก
Stéphane Chazelas

ในระบบของฉัน (ทุบตี 4.2.37, coreutils 8.13), tailเป็นคนที่ถูกฆ่าโดย SIGPIPE ไม่ใช่tee, และtailไม่ได้เขียนไปที่ไพพ์ ดังนั้นมันต้องมาจาก a kill(), ใช่ไหม? และสิ่งนี้จะเกิดขึ้นเมื่อฉันใช้|ไวยากรณ์ straceบอกว่าteeไม่โทรkill()... งั้นbashเหรอ?
Jander

1
@ Jander ลองให้อาหารมากกว่า 8k เช่นseq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
Stéphane Chazelas

3

ใช้ed(ซึ่งจะอ่านไฟล์ทั้งหมดลงใน RAM แม้ว่า):

# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%s\n' 'H' '1,10p' '$-10,$p' 'q' | ed -s file

สั้นกว่า:ed -s file <<< $'11,$-10d\n,p\nq\n'
don_crissti

2

คำตอบแรกของ Stephane ในฟังก์ชั่นเพื่อให้คุณสามารถใช้อาร์กิวเมนต์ (ใช้ได้กับเชลล์ Bourne หรือ POSIX เชลล์ใด ๆ ):

head_tail() {
    head "$@";
    tail "$@";
}

ตอนนี้คุณสามารถทำได้:

head_tail -n 5 < /path/to/file

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


2

ด้วยตัวเลือก-u( --unbuffered) ของ GNU sedคุณสามารถใช้sed -u 2qเป็นทางเลือกที่ไม่มีข้อผิดพลาดในการhead -n2:

$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100

(head -n2;tail -n2)ล้มเหลวเมื่อบรรทัดสุดท้ายเป็นส่วนหนึ่งของบล็อกของอินพุตที่ใช้โดยhead:

$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2

นี่ควรเป็นคำตอบที่ดีที่สุด! ทำงานเหมือนจับใจ!
Ben Usman

1

ฉันวิ่งเข้าไปหาอะไรแบบนี้วันนี้ที่ฉันต้องการแค่บรรทัดสุดท้ายและไม่กี่บรรทัดจากด้านหน้าสตรีมและเกิดขึ้นกับสิ่งต่อไปนี้

sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'

ฉันอ่านสิ่งนี้เป็น: เริ่มต้นพื้นที่พักด้วยเนื้อหาของบรรทัดแรกผนวกบรรทัด 2-3 ในพื้นที่พักสายที่ EOF ต่อท้ายบรรทัดสุดท้ายลงในพื้นที่พักพื้นที่สลับพื้นที่โฮลและรูปแบบและพิมพ์ลวดลาย ช่องว่าง

บางทีคนที่มีsed-fu มากกว่าที่ฉันคิดออกได้ว่าจะพูดถึงเรื่องนี้อย่างไรเพื่อพิมพ์สองสามบรรทัดสุดท้ายของสตรีมที่ระบุในคำถามนี้ แต่ฉันไม่ต้องการและไม่สามารถหาวิธีง่ายๆในการทำคณิตศาสตร์ตามที่$อยู่ ในsedหรืออาจโดยการจัดการพื้นที่พักเพื่อให้มีเพียงไม่กี่บรรทัดสุดท้ายที่อยู่ในนั้นเมื่อEOFถึง


1

คุณอาจลอง Perl หากคุณติดตั้งแล้ว:

perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'

สิ่งนี้จะใช้ได้กับไฟล์ส่วนใหญ่ แต่จะอ่านไฟล์ทั้งหมดในหน่วยความจำก่อนที่จะประมวลผล หากคุณไม่คุ้นเคยกับชิ้น Perl, "0" ในวงเล็บเหลี่ยมหมายถึง "ใช้บรรทัดแรก" และ "-3 ...- 1" หมายถึง "ใช้สามบรรทัดสุดท้าย" คุณสามารถปรับทั้งสองอย่างตามความต้องการของคุณ หากคุณต้องการประมวลผลไฟล์ที่มีขนาดใหญ่มาก (สิ่งที่ 'ใหญ่' อาจขึ้นอยู่กับ RAM ของคุณและอาจเป็นขนาดที่แลกเปลี่ยน) คุณอาจต้องการ:

perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'

มันอาจจะค่อนข้างช้ากว่าเพราะมันทำให้การแบ่งซ้ำทุกครั้ง แต่มันขึ้นอยู่กับขนาดไฟล์

คำสั่งทั้งสองควรทำงานได้ทั้งในไพพ์และไฟล์ปกติ

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