สมมติว่าคุณมีไฟล์ txt คำสั่งในการดู 10 บรรทัดบนสุดและ 10 บรรทัดล่างพร้อมกันคืออะไร?
เช่นถ้าไฟล์มีความยาว 200 บรรทัดให้ดูบรรทัด 1-10 และ 190-200 ในครั้งเดียว
สมมติว่าคุณมีไฟล์ txt คำสั่งในการดู 10 บรรทัดบนสุดและ 10 บรรทัดล่างพร้อมกันคืออะไร?
เช่นถ้าไฟล์มีความยาว 200 บรรทัดให้ดูบรรทัด 1-10 และ 190-200 ในครั้งเดียว
คำตอบ:
คุณสามารถ:
(head; tail) < file.txt
และหากคุณจำเป็นต้องใช้ท่อด้วยเหตุผลบางประการดังนี้:
cat file.txt | (head; tail)
หมายเหตุ: จะพิมพ์บรรทัดที่ซ้ำกันหากจำนวนบรรทัดใน file.txt มีขนาดเล็กกว่าบรรทัดเริ่มต้นของ head + บรรทัดเริ่มต้นของ tail
head
กินไฟล์ 10 บรรทัดแรกไปแล้ว (เปรียบเทียบกับhead < file.txt; tail < file.txt
ไฟล์ที่มีน้อยกว่า 20 บรรทัด) เป็นเพียงจุดเล็ก ๆ น้อย ๆ ที่ควรทราบ (แต่ยังคง +1)
head
เฉพาะ10 บรรทัดแรกของอินพุต แต่ก็ไม่มีการรับประกันว่าจะไม่กินมากขึ้นเพื่อค้นหาการสิ้นสุดบรรทัดที่ 10 โดยเหลืออินพุตไว้ให้แสดงน้อยลง less
seq 100 | (head; tail)
ให้ฉันเพียง 10 หมายเลขแรก เฉพาะขนาดอินพุตที่ใหญ่กว่ามากเท่านั้น (เช่นseq 2000
) tail เท่านั้นที่ได้รับอินพุต
สำหรับสตรีมที่บริสุทธิ์ (เช่นเอาต์พุตจากคำสั่ง) คุณสามารถใช้ 'tee' เพื่อแยกสตรีมและส่งหนึ่งสตรีมไปยังส่วนหัวและหนึ่งต่อท้าย สิ่งนี้ต้องใช้คุณสมบัติ '> (list)' ของ bash (+ / dev / fd / N):
( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )
หรือใช้ / dev / fd / N (หรือ / dev / stderr) บวกกับ subshells ที่มีการเปลี่ยนเส้นทางที่ซับซ้อน:
( ( seq 1 100 | tee /dev/fd/2 | head 1>&3 ) 2>&1 | tail ) 3>&1
( ( seq 1 100 | tee /dev/stderr | head 1>&3 ) 2>&1 | tail ) 3>&1
(สิ่งเหล่านี้จะไม่ทำงานใน csh หรือ tcsh)
สำหรับบางสิ่งที่มีการควบคุมที่ดีขึ้นเล็กน้อยคุณสามารถใช้คำสั่ง perl นี้:
COMMAND | perl -e 'my $size = 10; my @buf = (); while (<>) { print if $. <= $size; push(@buf, $_); if ( @buf > $size ) { shift(@buf); } } print "------\n"; print @buf;'
COMMAND | { tee >(head >&2) | tail; } |& other_commands
cat >/dev/null
แก้ไข:COMMAND | { tee >(head >&2; cat >/dev/null) | tail; } |& other_commands
head
และtail
คำสั่ง: \ ...
(sed -u 10q; echo ...; tail) < file.txt
รูปแบบอื่นใน(head;tail)
ธีม แต่หลีกเลี่ยงปัญหาการเติมบัฟเฟอร์เริ่มต้นสำหรับไฟล์ขนาดเล็ก
head -10 file.txt; tail -10 file.txt
นอกเหนือจากนั้นคุณจะต้องเขียนโปรแกรม / สคริปต์ของคุณเอง
cat
และhead
หรือใช้tail
ท่อมาโดยตลอดดีใจที่รู้ว่าฉันสามารถใช้ทีละรายการได้!
{ head file; tail file; } | prog
(ต้องเว้นระยะห่างภายในวงเล็บปีกกาและต้องมีอัฒภาคต่อท้าย)
จากความคิดเห็นของ JF Sebastian :
cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1
ด้วยวิธีนี้คุณสามารถประมวลผลบรรทัดแรกและส่วนที่เหลือแตกต่างกันในท่อเดียวซึ่งมีประโยชน์สำหรับการทำงานกับข้อมูล CSV:
{ echo N; seq 3;} | { tee >(head -n1 | sed 's/$/*2/' >&3; cat >/dev/null) | tail -n+2 | awk '{print $1*2}'; } 3>&1
N * 2 2 4 6
ปัญหาคือโปรแกรมที่เน้นสตรีมไม่ทราบความยาวของไฟล์ล่วงหน้า (เนื่องจากอาจไม่มีไฟล์หากเป็นสตรีมจริง)
เครื่องมือเช่น tail
บัฟเฟอร์ n บรรทัดสุดท้ายที่เห็นและรอให้สิ้นสุดสตรีมจากนั้นพิมพ์
หากคุณต้องการทำสิ่งนี้ในคำสั่งเดียว (และให้มันทำงานกับออฟเซ็ตใด ๆ และอย่าทำซ้ำบรรทัดหากมันทับซ้อนกัน) คุณจะต้องเลียนแบบพฤติกรรมนี้ที่ฉันพูดถึง
ลอง awk นี้:
awk -v offset=10 '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' yourfile
a.out | awk -v ...
ต้องใช้เวลานานพอสมควรในการจบลงด้วยโซลูชันนี้ซึ่งดูเหมือนจะเป็นวิธีเดียวที่ครอบคลุมกรณีการใช้งานทั้งหมด (จนถึงตอนนี้):
command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \
'{
if (NR <= offset) print;
else {
a[NR] = $0;
delete a[NR-offset];
printf "." > "/dev/stderr"
}
}
END {
print "" > "/dev/stderr";
for(i=NR-offset+1 > offset ? NR-offset+1: offset+1 ;i<=NR;i++)
{ print a[i]}
}'
รายการคุณสมบัติ:
ฉันมองหาวิธีแก้ปัญหานี้มาระยะหนึ่งแล้ว พยายามด้วยตัวเอง แต่ปัญหาเกี่ยวกับการไม่ทราบความยาวของไฟล์ / สตรีมก่อนล่วงหน้านั้นผ่านไม่ได้ จากตัวเลือกทั้งหมดที่มีอยู่ข้างต้นฉันชอบโซลูชัน awk ของ Camille Goudeseune เขาจดบันทึกว่าโซลูชันของเขาทิ้งบรรทัดว่างพิเศษไว้ในเอาต์พุตพร้อมชุดข้อมูลขนาดเล็กเพียงพอ ที่นี่ฉันจัดเตรียมการปรับเปลี่ยนโซลูชันของเขาที่ลบบรรทัดพิเศษ
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { a_count=0; for (i in a) {a_count++}; for (i=NR-a_count+1; i<=NR; i++) print a[i] }' ; }
คุณสามารถผูกมันเข้าด้วยกันได้เสมอ เช่นนั้น,
head fiename_foo && tail filename_foo
. หากยังไม่เพียงพอคุณสามารถเขียนฟังก์ชัน bash ในไฟล์. profile หรือไฟล์ล็อกอินที่คุณใช้:
head_and_tail() {
head $1 && tail $1
}
และเรียกใช้ในภายหลังจากพร้อมต์เชลล์ของคุณ: head_and_tail filename_foo
.
10 บรรทัดแรกของ file.ext จากนั้น 10 บรรทัดสุดท้าย:
cat file.ext | head -10 && cat file.ext | tail -10
10 บรรทัดสุดท้ายของไฟล์จากนั้น 10 บรรทัดแรก:
cat file.ext | tail -10 && cat file.ext | head -10
จากนั้นคุณสามารถไปป์เอาต์พุตที่อื่นได้เช่นกัน:
(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program
tail
และhead
หรือฟังก์ชั่นโดยใช้นามแฝง
ฉันเขียนแอพ python ง่ายๆเพื่อทำสิ่งนี้: https://gist.github.com/garyvdm/9970522
จัดการไปป์ (สตรีม) เช่นเดียวกับไฟล์
วาดตามแนวคิดข้างต้น (ทดสอบ bash & zsh)
แต่ใช้นามแฝงว่า Head and Tails
alias hat='(head -5 && echo "^^^------vvv" && tail -5) < '
hat large.sql
ทำไมไม่ใช้sed
สำหรับงานนี้?
sed -n -e 1,+9p -e 190,+9p textfile.txt
ในการจัดการไปป์ (สตรีม) และไฟล์ให้เพิ่มสิ่งนี้ในไฟล์. bashrc หรือ. profile ของคุณ:
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' ; }
จากนั้นคุณสามารถไม่เพียง
headtail 10 < file.txt
แต่ยัง
a.out | headtail 10
(สิ่งนี้ยังคงต่อท้ายบรรทัดว่างปลอมเมื่อ 10 เกินความยาวของอินพุตซึ่งแตกต่างจากแบบเก่าa.out | (head; tail)
ขอบคุณผู้ตอบก่อนหน้านี้)
หมายเหตุ: ไม่headtail 10
headtail -10
จากสิ่งที่ @Samus_ อธิบายที่นี่เกี่ยวกับวิธีการทำงานของคำสั่งของ @Aleksandra Zalcman รูปแบบนี้มีประโยชน์เมื่อคุณไม่สามารถระบุตำแหน่งที่หางเริ่มต้นได้อย่างรวดเร็วโดยไม่ต้องนับเส้น
{ head; echo "####################\n...\n####################"; tail; } < file.txt
หรือถ้าคุณเริ่มทำงานกับอย่างอื่นที่ไม่ใช่ 20 บรรทัดการนับบรรทัดอาจช่วยได้
{ head -n 18; tail -n 14; } < file.txt | cat -n
ในการพิมพ์ 10 บรรทัดแรกและ 10 บรรทัดสุดท้ายของไฟล์คุณสามารถลองสิ่งนี้:
cat <(head -n10 file.txt) <(tail -n10 file.txt) | less
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"
หมายเหตุ : ผู้aFileตัวแปรมีแฟ้มเส้นทางแบบเต็ม
ฉันจะบอกว่าขึ้นอยู่กับขนาดของไฟล์การอ่านเนื้อหาอย่างกระตือรือร้นอาจไม่เป็นที่ต้องการ ในกรณีนั้นฉันคิดว่าเชลล์สคริปต์แบบง่าย ๆ ก็น่าจะเพียงพอแล้ว
ต่อไปนี้เป็นวิธีที่ฉันเพิ่งจัดการกับไฟล์ CSV ขนาดใหญ่จำนวนมากที่ฉันกำลังวิเคราะห์:
$ for file in *.csv; do echo "### ${file}" && head ${file} && echo ... && tail ${file} && echo; done
ซึ่งจะพิมพ์ 10 บรรทัดแรกและ 10 บรรทัดสุดท้ายของแต่ละไฟล์ในขณะที่พิมพ์ชื่อไฟล์และจุดไข่ปลาก่อนและหลัง
สำหรับไฟล์ขนาดใหญ่ไฟล์เดียวคุณสามารถเรียกใช้สิ่งต่อไปนี้เพื่อให้ได้เอฟเฟกต์เดียวกัน:
$ head somefile.csv && echo ... && tail somefile.csv
สิ้นเปลือง stdin แต่เรียบง่ายและใช้ได้กับ 99% ของกรณีการใช้งาน
#!/usr/bin/env bash
COUNT=${1:-10}
IT=$(cat /dev/stdin)
echo "$IT" | head -n$COUNT
echo "..."
echo "$IT" | tail -n$COUNT
$ seq 100 | head_and_tail 4
1
2
3
4
...
97
98
99
100