คำสั่ง unix ด่วนเพื่อแสดงบรรทัดเฉพาะกลางไฟล์?


206

การพยายามแก้ไขข้อบกพร่องของเซิร์ฟเวอร์และไฟล์บันทึกของฉันเพียงไฟล์เดียวคือไฟล์บันทึก 20GB (โดยไม่มีการประทับเวลาแม้แต่คนใช้System.out.println()การบันทึกในการผลิตทำไม?)

เมื่อใช้ grep ฉันพบพื้นที่ของไฟล์ที่ฉันต้องการดูบรรทัด 347340107

นอกเหนือจากการทำสิ่งที่ชอบ

head -<$LINENUM + 10> filename | tail -20 

... ซึ่งจะต้องheadอ่าน 347 ล้านบรรทัดแรกของไฟล์บันทึกมีคำสั่งที่ง่ายและรวดเร็วที่จะถ่ายโอนข้อมูลบรรทัด 347340100 - 347340200 (ตัวอย่าง) ไปยังคอนโซลหรือไม่

อัปเดตฉันลืมไปเลยว่า grep สามารถพิมพ์บริบทรอบ ๆ การแข่งขัน ... ได้ผลดี ขอบคุณ!


ฉันคิดว่า grep ต้องค้นหาไฟล์ทั้งหมดจะต้องมีซีพียูน้อยลงวิธีการทำเช่นนี้
ojblass

คำตอบ:


69

ด้วย GNU-grep คุณสามารถพูดได้

grep --context = 10 ...

7
หรือมากกว่านั้นโดยเฉพาะ 10 บรรทัดก่อน: grep -B 10 ... หรือ 10 บรรทัดหลังจาก: grep -A 10 ...
Boy Baukema

17
คำสั่งนี้ใช้งานไม่ได้ด้านล่าง sed -n '<start>, <end> p' กำลังทำงาน
Basav

5
นี่ไม่ใช่สิ่งที่คุณต้องการเพราะมันจะประมวลผลไฟล์ทั้งหมดแม้ว่าการแข่งขันจะอยู่ในอันดับต้น ๆ ก็ตาม ณ จุดนี้คำสั่งผสมหัว / หางหรือหาง / หัวมีประสิทธิภาพมากขึ้น
Sklivvz

3
สิ่งนี้ไม่ตอบคำถามที่ถามเลยเนื่องจากวิธีนี้ไม่มีวิธีการส่งออกบรรทัดเฉพาะตามที่ถาม
Chris Rasys

1
นี่ไม่ใช่สิ่งที่ถูกถาม @ แมทขทำไมคุณไม่ยกเลิกคำตอบนี้ล่ะ?
user1271772

390

ฉันพบวิธีแก้ไขปัญหาอื่นสองวิธีถ้าคุณทราบหมายเลขบรรทัด แต่ไม่มีอะไรอื่น (ไม่มี grep เป็นไปได้):

สมมติว่าคุณต้องการบรรทัด 20 ถึง 40

sed -n '20,40p;41q' file_name

หรือ

awk 'FNR>=20 && FNR<=40' file_name

6
+1: แม้ว่าคุณอาจต้องการออกจากหลังจากพิมพ์ อาจให้ประโยชน์ด้านประสิทธิภาพหากไฟล์มีขนาดใหญ่มาก
jaypal singh

awk 'NR> = 20 && NR <= 40' file_name
Sudipta Basak

2
sed -n '20, 40p; 41q 'file_name สำหรับออกแล้ว
Snigdha Batra

1
โดยเฉพาะตัวเลขเหล่านั้นคือหมายเลขเริ่มต้นและสิ้นสุด หากคุณอยู่ในไฟล์ที่ใหญ่กว่าจะเป็น '12345678,12345699p'
Code Abominator

1
นอกจากนี้ยัง @ CodeAbominator ของความคิดเห็น41qสั่ง sed จะเลิก41เมื่อสาย
Brice

116
# print line number 52
sed -n '52p' # method 1
sed '52!d' # method 2
sed '52q;d' # method 3,  efficient on large files 

วิธีที่ 3 มีประสิทธิภาพสำหรับไฟล์ขนาดใหญ่

วิธีที่เร็วที่สุดในการแสดงบรรทัดเฉพาะ


ฉันพยายามหาวิธีปรับวิธีที่ 3 เพื่อใช้ช่วงแทนที่จะเป็นบรรทัดเดียว แต่ฉันเกรงว่าฉันจะไม่ทำงาน
Xiong Chiamiov

9
@XiongChiamiov แล้ว sed -n '1,500p; 501q' สำหรับการพิมพ์ 1-500 ยังไง?
Sam

3
เหตุผลที่สองบรรทัดแรก / วิธีการมีประสิทธิภาพน้อยลงคือพวกเขาดำเนินการประมวลผลทุกบรรทัดหลังจาก Line 52 จนถึงจุดสิ้นสุดในขณะที่ # 3 หยุดหลังจากพิมพ์ Line 52
flow2k

1
คำตอบนี้จะได้รับประโยชน์จากการอธิบายสิ่งที่อาร์กิวเมนต์ทั้งหมดทำ
Bram Vanroy

25

ไม่มีไม่มีไฟล์ไม่สามารถกำหนดแอดเดรสได้

ไม่มีวิธีคงที่ในการค้นหาจุดเริ่มต้นของบรรทัดnในไฟล์ข้อความ คุณต้องสตรีมผ่านไฟล์และนับบรรทัดใหม่

ใช้เครื่องมือที่ง่ายที่สุด / เร็วที่สุดที่คุณต้องทำ ให้ฉันใช้headทำให้มากความรู้สึกมากกว่าgrepตั้งแต่หลังมีความซับซ้อนทาง ฉันไม่ได้พูดว่า " grepช้า" จริง ๆ แล้วไม่ใช่ แต่ฉันจะแปลกใจถ้ามันเร็วกว่าheadสำหรับกรณีนี้ นั่นเป็นข้อผิดพลาดheadโดยทั่วไป


2
เว้นแต่ว่าบรรทัดนั้นมีความกว้างคงที่ในหน่วยไบต์คุณจะไม่ทราบว่าจะย้ายตัวชี้ไฟล์ไปที่ใดโดยไม่นับอักขระบรรทัดใหม่จากจุดเริ่มต้นของไฟล์
Joseph Lust

สิ่งนี้ไม่ได้ให้คำตอบสำหรับคำถาม หากต้องการวิจารณ์หรือขอคำชี้แจงจากผู้แต่งโปรดแสดงความคิดเห็นใต้โพสต์ของพวกเขา
exhuma

@ exhuma คุณพูดถูก ฉันเขียนใหม่ เจ็ดปีที่ผ่านมาฉันเมาแล้ว :)
คลาย

20

เกี่ยวกับ:

tail -n +347340107 filename | head -n 100

ฉันไม่ได้ทดสอบ แต่ฉันคิดว่ามันจะได้ผล


ไม่ปกติแล้วหางมีขีด จำกัด 256 กิโลไบต์สุดท้ายหรือคล้ายกันขึ้นอยู่กับรุ่นและระบบปฏิบัติการ
Antti Rytsölä

💪 yessire miller
dctremblay

13

ฉันชอบที่จะเข้าไปlessและ

  • พิมพ์50%เพื่อข้ามไปครึ่งไฟล์
  • 43210G เพื่อไปที่บรรทัด 43210
  • :43210 ทำเช่นเดียวกัน

และสิ่งเช่นนั้น

ดียิ่งขึ้น: กดvเพื่อเริ่มการแก้ไข (ในเสียงเรียกเข้าแน่นอน!) ที่ตำแหน่งนั้น ตอนนี้ให้สังเกตว่าvimมีการเชื่อมโยงคีย์เดียวกัน!


12

ก่อนอื่นฉันจะแบ่งไฟล์ออกเป็นไฟล์เล็ก ๆ น้อย ๆ เช่นนี้

$ split --lines=50000 /path/to/large/file /path/to/output/file/prefix

จากนั้น grep บนไฟล์ผลลัพธ์


เห็นด้วยให้ทำลายการบันทึกและสร้างงาน cron เพื่อทำอย่างถูกต้อง ใช้ logrotate หรือสิ่งที่คล้ายกันเพื่อไม่ให้มันใหญ่โต
Tanj

9

คุณสามารถใช้exคำสั่งโปรแกรมแก้ไข Unix มาตรฐาน (ตอนนี้เป็นส่วนหนึ่งของ Vim) เช่น

  • แสดงบรรทัดเดียว (เช่นสายที่สอง):

    ex +2p -scq file.txt

    ไวยากรณ์ sed ที่สอดคล้องกัน: sed -n '2p' file.txt

  • ช่วงของเส้น (เช่น 2-5 เส้น):

    ex +2,5p -scq file.txt

    sed sed: sed -n '2,5p' file.txt

  • จากบรรทัดที่กำหนดจนถึงจุดสิ้นสุด (เช่น 5 ถึงจุดสิ้นสุดของไฟล์):

    ex +5,p -scq file.txt

    sed sed: sed -n '2,$p' file.txt

  • ช่วงหลายบรรทัด (เช่น 2-4 และ 6-8 บรรทัด):

    ex +2,4p +6,8p -scq file.txt

    sed sed: sed -n '2,4p;6,8p' file.txt

คำสั่งข้างต้นสามารถทดสอบได้ด้วยไฟล์ทดสอบต่อไปนี้:

seq 1 20 > file.txt

คำอธิบาย:

  • +หรือ-cตามด้วยคำสั่ง - ดำเนินการคำสั่ง (vi / vim) หลังจากอ่านไฟล์แล้ว
  • -s - โหมดเงียบยังใช้เทอร์มินัลปัจจุบันเป็นเอาต์พุตเริ่มต้น
  • qตามด้วย-cคำสั่งเพื่อออกจากตัวแก้ไข (เพิ่ม!สิ่งที่ต้องทำออกจากแรงเช่น-scq!)


6

ได้รับ ack

การติดตั้ง Ubuntu / Debian:

$ sudo apt-get install ack-grep

จากนั้นเรียกใช้:

$ ack --lines=$START-$END filename

ตัวอย่าง:

$ ack --lines=10-20 filename

จาก$ man ack:

--lines=NUM
    Only print line NUM of each file. Multiple lines can be given with multiple --lines options or as a comma separated list (--lines=3,5,7). --lines=4-7 also works. 
    The lines are always output in ascending order, no matter the order given on the command line.

1
นี่สำหรับฉันดูเหมือนว่าคำสั่งที่มีไวยากรณ์ที่ใช้งานง่ายที่สุดจากคำตอบทั้งหมดที่นี่
nzn

จากเวอร์ชัน 2.999_06 ในวันที่ 10 มกราคม 2019 --linesพารามิเตอร์จะถูกลบออก
ไหม้

4

sed จะต้องอ่านข้อมูลด้วยเพื่อนับจำนวนบรรทัด วิธีเดียวที่จะเป็นไปได้ทางลัดก็คือจะต้องมีบริบท / คำสั่งในไฟล์เพื่อดำเนินการ ตัวอย่างเช่นหากมีบรรทัดบันทึกที่มีการเติมวันที่ความกว้างคงที่และอื่น ๆ คุณสามารถใช้ยูทิลิตี้look unix เพื่อค้นหาไบนารี่ผ่านไฟล์สำหรับวันที่ / เวลาที่เจาะจง


4

ใช้

x=`cat -n <file> | grep <match> | awk '{print $1}'`

ที่นี่คุณจะได้รับหมายเลขบรรทัดที่มีการแข่งขันเกิดขึ้น

ตอนนี้คุณสามารถใช้คำสั่งต่อไปนี้เพื่อพิมพ์ 100 บรรทัด

awk -v var="$x" 'NR>=var && NR<=var+100{print}' <file>

หรือคุณสามารถใช้ "sed" เช่นกัน

sed -n "${x},${x+100}p" <file>

หากคุณมีมากกว่าหนึ่งคู่ให้ใช้: "awk 'NR == 1 {พิมพ์ $ 1}" สำหรับนัดแรกและอื่น ๆ
Ramana Reddy

2

ด้วยการที่sed -e '1,N d; M q'คุณจะพิมพ์บรรทัด N + 1 ถึง M นี่อาจจะดีกว่าสักเล็กน้อยgrep -Cเพราะมันจะไม่พยายามจับคู่ลายเส้นกับลวดลาย


-eเป็นตัวเลือกที่นี่
flow2k

2

คำตอบของ Sklivvz ต่อไปนี้เป็นฟังก์ชั่นที่ดีที่สามารถใส่ลงใน.bash_aliasesไฟล์ได้ มันมีประสิทธิภาพในไฟล์ขนาดใหญ่เมื่อพิมพ์สิ่งต่าง ๆ จากด้านหน้าของไฟล์

function middle()
{
    startidx=$1
    len=$2
    endidx=$(($startidx+$len))
    filename=$3

    awk "FNR>=${startidx} && FNR<=${endidx} { print NR\" \"\$0 }; FNR>${endidx} { print \"END HERE\"; exit }" $filename
}

1

หากต้องการแสดงเส้นได้<textfile>โดยตัวของมัน<line#>เพียงแค่ทำสิ่งนี้:

perl -wne 'print if $. == <line#>' <textfile>

หากคุณต้องการวิธีที่มีประสิทธิภาพมากขึ้นในการแสดงช่วงของบรรทัดที่มีการแสดงออกปกติ - ฉันจะไม่พูดว่าทำไม grep เป็นความคิดที่ไม่ดีสำหรับการทำเช่นนี้มันควรจะชัดเจนพอสมควร - การแสดงออกอย่างง่ายนี้จะแสดงช่วงของคุณใน ซิงเกิ้ลพาสซึ่งเป็นสิ่งที่คุณต้องการเมื่อจัดการกับไฟล์ข้อความ ~ 20GB:

perl -wne 'print if m/<regex1>/ .. m/<regex2>/' <filename>

(เคล็ดลับ: ถ้า regex ของคุณมี/อยู่ให้ใช้บางอย่างm!<regex>!แทน)

นี้จะพิมพ์ออกมา<filename>เริ่มต้นด้วยเส้นที่ตรงกับ<regex1>ขึ้นจน (และรวม) <regex2>เส้นที่ตรงกับ

ไม่ต้องใช้ตัวช่วยสร้างเพื่อดูว่าการปรับแต่งเล็กน้อยสามารถทำให้มีประสิทธิภาพยิ่งขึ้นได้อย่างไร

สิ่งสุดท้าย: Perl เพราะมันเป็นภาษาผู้ใหญ่มีการปรับปรุงที่ซ่อนอยู่มากมายเพื่อสนับสนุนความเร็วและประสิทธิภาพ ด้วยความคำนึงถึงสิ่งนี้จึงทำให้เป็นตัวเลือกที่ชัดเจนสำหรับการดำเนินการดังกล่าวเนื่องจากได้รับการพัฒนาเพื่อจัดการกับไฟล์บันทึกข้อความฐานข้อมูลขนาดใหญ่เป็นต้น


จริงๆแล้วมันไม่ได้เป็นอย่างนั้นสำหรับฉันเนื่องจากเมื่อมีการเรียกใช้คำสั่ง perl หนึ่งครั้งที่ซับซ้อนกว่าการพูดการเรียกใช้โปรแกรม 2+ ไปป์ไลน์ด้วยกัน (เพิ่มเติมลงไปที่หน้า) และฉันคิดว่าคุณกำลังพูดจริง ๆ คำอธิบายที่ต้องการให้คุณอ่านเนื่องจากมีความซับซ้อนเท่ากัน (หรือมากกว่า) ลงในหน้าเว็บที่ไม่ได้ถูกปลิวไปจากน้ำ ... sheesh
osirisgothra

โปรดทราบว่าผู้ใช้ขอช่วงของเส้น - ตัวอย่างของคุณสามารถปรับได้เล็กน้อย
Sklivvz


0

ง่ายด้วย Perl! หากคุณต้องการรับบรรทัด 1, 3 และ 5 จากไฟล์ให้พูด / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

1
คุณบอกว่าเป็นเรื่องง่ายด้วย awk แต่คุณทำมันใน Perl แทน?
นักโทษ 13

0

ฉันประหลาดใจเพียงคำตอบเดียว (โดย Ramana Reddy) แนะนำให้เพิ่มหมายเลขบรรทัดในผลลัพธ์ ต่อไปนี้ค้นหาหมายเลขบรรทัดที่ต้องการและสีเอาท์พุท

file=FILE
lineno=LINENO
wb="107"; bf="30;1"; rb="101"; yb="103"
cat -n ${file} | { GREP_COLORS="se=${wb};${bf}:cx=${wb};${bf}:ms=${rb};${bf}:sl=${yb};${bf}" grep --color -C 10 "^[[:space:]]\\+${lineno}[[:space:]]"; }

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