ค้นหาการเกิดขึ้นทั้งหมดในไฟล์ที่มี sed


15

การใช้ OPEN STEP 4.2 OS ... ฉันกำลังใช้sedคำสั่งต่อไปนี้:

sed -n '1,/141.299.99.1/p' TESTFILE | tail -3

คำสั่งนี้จะค้นหาหนึ่งอินสแตนซ์ในไฟล์ที่มี ip ของ 141.299.99.1 และรวม 3 บรรทัดก่อนที่มันจะดีทั้งหมดยกเว้นว่าฉันต้องการหาอินสแตนซ์ทั้งหมดของ IP และ 3 บรรทัดก่อนหน้าด้วย ไม่ใช่แค่คนแรก


1
กรุณาเสมอรวมถึงระบบปฏิบัติการของคุณ การแก้ปัญหามักขึ้นอยู่กับระบบปฏิบัติการที่ใช้ คุณใช้ Unix, Linux, BSD, OSX หรือเปล่า เวอร์ชันไหน
terdon

จุดที่ดี! การใช้ Open Step เวอร์ชั่น 4.2 ค่อนข้างเก่าและเชลล์ที่รวมมานั้นไม่ได้รวมคุณสมบัติหลายอย่างที่ระบุไว้ในคำตอบด้านล่าง
Dale

จากความอยากรู้ - ระบบ OPEN STEP 4.2 คืออะไรและใช้สำหรับอะไรในวันนี้
Thorbjørn Ravn Andersen

(และถ้า Perl ใช้ได้จริงๆคุณสามารถทำสิ่งต่างๆมากมายแค่ดีกับที่)
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersenอาจจะเป็นแบบนี้: en.wikipedia.org/wiki/OpenStep
Barmar

คำตอบ:


4

นี่คือความพยายามที่จะเลียนแบบgrep -B3โดยใช้หน้าต่างที่เคลื่อนไหวอยู่บนพื้นฐานของตัวอย่างของ GNU sed (แต่หวังว่าจะเป็นไปตาม POSIX - ด้วยการรับทราบถึง @ StéphaneChazelas):

sed -e '1h;2,4{;H;g;}' -e '1,3d' -e '/141\.299\.99\.1/P' -e '$!N;D' file

สองนิพจน์แรกจะใช้บัฟเฟอร์รูปแบบหลายบรรทัดและอนุญาตให้จัดการกับตัวพิมพ์ขอบซึ่งมีบริบทน้อยกว่า 3 บรรทัดก่อนหน้าบริบทก่อนการแข่งขันครั้งแรก นิพจน์ตรงกลาง (จับคู่ regex) พิมพ์บรรทัดปิดที่ด้านบนของหน้าต่างจนกว่าข้อความการจับคู่ที่ต้องการจะกระเพื่อมขึ้นผ่านบัฟเฟอร์รูปแบบ สุดท้าย$!N;Dเลื่อนหน้าต่างทีละบรรทัดยกเว้นเมื่อถึงจุดสิ้นสุดของอินพุต


-eไม่เฉพาะ GNU ในการเป็น POSIX / พกพาคุณจำเป็นต้องใช้เพราะไม่มีอะไรหลังจาก}(และคุณต้องการ;ก่อน)
Stéphane Chazelas

ขอบคุณ @ StéphaneChazelas - คุณจะบอกว่าเป็น POSIX / พกพากลุ่มแรกจะต้องแยก / แก้ไขเป็น-e '1h;2,4{H;g;}' -e '1,3d'? ฉันไม่มีระบบที่ไม่ใช่ของ GNU เพื่อทำการทดสอบ (และ--posixสวิตช์ของ GNU sed ดูเหมือนจะไม่สนใจ)
ขับขี่เหล็ก

1
ใช่บน Linux คุณสามารถทดสอบการใช้งานที่แตกต่างsedจากชุดเครื่องมือมรดกซึ่งเป็นลูกหลานของ Unix sed แบบดั้งเดิม ข้อมูลจำเพาะ POSIX / Unix สำหรับsedอยู่ที่pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
Stéphane Chazelas

ฉันได้รับเหตุการณ์ที่ไม่พบในสิ่งเหล่านี้: N; D ': ไม่พบเหตุการณ์ ฉันไม่มีไวยากรณ์อยู่หรือเปล่า ขอบคุณ !!
Dale

ขออภัยฉันเพิ่งรู้ว่าการแก้ไขครั้งล่าสุดของฉันตัดการเสนอราคาปิดครั้งเดียวหลังจากนิพจน์แรก -e ฉันได้แก้ไขแล้ว - คุณลองอีกครั้งด้วยนิพจน์ด้านบนได้ไหม
ขับขี่เหล็ก

10

grep จะทำงานได้ดีขึ้นในเรื่องนี้:

grep -B 3 141.299.99.1 TESTFILE

-B 3วิธีการพิมพ์สามบรรทัดก่อนการแข่งขันแต่ละครั้ง สิ่งนี้จะพิมพ์--ระหว่างแต่ละกลุ่มของบรรทัด หากต้องการปิดใช้งานให้ใช้--no-group-separatorเช่นกัน

-Bตัวเลือกที่ได้รับการสนับสนุนโดยGNUgrepและมากที่สุดในรุ่น BSD เช่นกัน ( OSX , FreeBSD , OpenBSD , NetBSD ) แต่มันเป็นเทคนิคที่ไม่เป็นตัวเลือกที่ได้มาตรฐาน


1
Michael Homer - ขอบคุณ ฉันไม่มีตัวเลือก - B ความคิดอื่น ๆ ?
Dale

@Dale คุณสามารถติดตั้ง GNU grep ได้หรือไม่ ที่จะให้ตัวเลือกแก่คุณ
Barmar

9

ด้วยsedคุณสามารถทำหน้าต่างบานเลื่อน

sed '1N;$!N;/141.299.99.1/P;D'

นั่นมัน แต่ระวังbashพฤติกรรมบ้าของการขยายตัว! แม้จะยกมา !!! ในสตริงคำสั่งจากประวัติคำสั่งของคุณอาจทำให้มันบ้าไปหน่อย นำหน้าคำสั่งด้วยset +H;หากคุณพบว่าเป็นกรณีนี้ หากต้องการเปิดใช้งานอีกครั้ง(แต่ทำไม ???)ทำในset -Hภายหลัง

ที่แน่นอนเท่านั้นจะใช้ถ้าคุณถูกใช้bash- แต่ผมไม่เชื่อว่าคุณเป็น ผมค่อนข้างมั่นใจคุณกำลังทำงานกับcsh- (ซึ่งเกิดขึ้นเป็นเปลือกที่มีพฤติกรรมบ้าbashemulates กับการขยายตัวประวัติศาสตร์ แต่อาจจะไม่สุดขั้วคเปลือกเอามัน) ดังนั้นอาจ\!ควรจะทำงาน ฉันหวังว่า.

มันเป็นโค้ดแบบพกพาทั้งหมด: POSIX อธิบายประกอบการสามดังนี้: (แม้ว่ามันจะเป็นที่น่าสังเกตว่าฉันได้รับการยืนยันเพียงคำอธิบายนี้มีอยู่เป็นช่วงต้น 2001)

[2addr]N ผนวกอินพุตบรรทัดถัดไปโดยลด\newline ที่จะสิ้นสุดลงในพื้นที่รูปแบบโดยใช้\newline ในตัวเพื่อแยกวัสดุที่ต่อท้ายออกจากวัสดุดั้งเดิม โปรดทราบว่าการเปลี่ยนแปลงหมายเลขบรรทัดปัจจุบัน

[2addr]P เขียนพื้นที่รูปแบบจนถึง\newline แรกไปยังเอาต์พุตมาตรฐาน

[2addr]D ลบเซ็กเมนต์เริ่มต้นของพื้นที่รูปแบบผ่าน\newline แรกและเริ่มรอบถัดไป

ดังนั้นในบรรทัดแรกคุณเพิ่มบรรทัดพิเศษลงในพื้นที่รูปแบบดังนั้นจึงมีลักษณะดังนี้:

^line 1s contents\nline 2s contents$

จากนั้นในบรรทัดแรกและทุกบรรทัดหลังจากนั้น - ยกเว้นบรรทัดสุดท้าย - คุณเพิ่มอีกบรรทัดในพื้นที่รูปแบบ ดังนั้นดูเหมือนว่านี้:

^line 1\nline 2\nline 3$

หากพบที่อยู่ IP ของคุณภายในคุณPจะขึ้นบรรทัดใหม่เป็นครั้งแรกดังนั้นเพียงแค่บรรทัดที่ 1 ที่นี่ ในตอนท้ายของทุกรอบคุณจะได้พบDกันและเริ่มต้นใหม่ด้วยสิ่งที่เหลืออยู่ ดังนั้นรอบต่อไปดูเหมือนว่า:

^line 2\nline 3\nline 4$

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

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

seq 10 52 | sed '1N;$!N;/0\(\n\|$\)/P;D'

10
18
19
20
28
29
30
38
39
40
48
49
50

คนนั้นซับซ้อนกว่าคุณเล็กน้อยเพราะฉันต้องสลับจากการขึ้น0\nบรรทัดใหม่หรือ0$จุดสิ้นสุดของรูปแบบเพื่อให้คล้ายกับปัญหาของคุณมากขึ้น - แต่พวกเขาแตกต่างกันอย่างละเอียดในเรื่องนี้ต้องใช้สมอ - ซึ่งอาจเป็นเรื่องยากที่จะทำตั้งแต่ รูปแบบพื้นที่เลื่อนอย่างต่อเนื่อง

ฉันใช้กรณีแปลก ๆ ของ 10 และ 52 เพื่อแสดงว่าตราบใดที่สมอเรือมีความยืดหยุ่นดังนั้นก็คือผลลัพธ์ อย่างเต็มที่พกพาฉันสามารถบรรลุผลลัพธ์เดียวกันโดยแทนที่จะนับอัลกอริทึมและทำ:

seq 10 52 | sed '1N;$!N;/[90]\n/P;D'

และขยายการค้นหาในขณะที่ จำกัด หน้าต่างของฉัน - จาก 0 ถึง 9 และ 0 และจาก 3 บรรทัดเป็นสอง

อย่างไรก็ตามคุณได้รับความคิด


ขอบคุณสำหรับการทำงานหนักทั้งหมดของคุณ ขออภัยฉันจะใส่ชื่อไฟล์ที่ฉันต้องการค้นหาผ่านที่ไหน
Dale

@Dale - ฉันไม่ดี sed '...' $filename. โดยวิธี - ฉันทิ้งไว้ในช่วงเวลาจากสตริงการค้นหาของคุณเอง แต่สิ่งเหล่านั้นไม่ได้เป็นจุดในรูปแบบจริง ๆ - สิ่งเหล่านั้นแสดงถึงอักขระตัวเดียว คุณน่าจะทำoct\.oct\.oct\.octเพื่อหนีพวกมันเพื่อพวกมันจะจับคู่ช่วงเวลาเท่านั้น
mikeserv

ฉันพยายามที่จะจัดการกับมันและสัญลักษณ์ <> ที่แตกต่างกันและฉันไม่พบเหตุการณ์ที่ฉันได้รับจากโซลูชันอื่น ๆ ที่นี่ดังนั้นฉันจึงสงสัยว่าระบบปฏิบัติการของฉันไม่สามารถใช้งานร่วมกับโซลูชันเหล่านี้ได้
Dale

ตอนนี้ผลลัพธ์ด้วย -> N; /141.299.99.1/P; D ': ไม่พบเหตุการณ์
Dale

@Dale - โปรดดูการอัปเดต มันควรจะช่วยคุณ
mikeserv

4

เนื่องจากคุณพูดถึงว่าคุณไม่มี-Bตัวเลือกgrepคุณสามารถใช้ Perl (ตัวอย่าง) เพื่อสร้างการเลื่อนหน้าต่าง 4 บรรทัด:

perl -ne '
    push @window,$_;
    shift @window if @window > 4;
    print @window if /141\.299\.99\.1/
' your_file

คำตอบของ Rameshawkไม่สิ่งที่คล้ายกันกับ


ฉันไม่แน่ใจว่า Perl ของฉันรองรับเวอร์ชั่นนี้หรือไม่ แต่ฉันจะลองดู ขอบคุณมากที่สละเวลาตอบคำถามของฉัน - ขอบคุณมาก!
Dale

@ เดลคุณยินดีอย่างมาก ฉันสงสัยว่ารหัสนี้ใช้ประโยชน์จากคุณสมบัติ Perl ที่ล้ำสมัยใด ๆ
โจเซฟอาร์

4

เมื่อพร้อมใช้งานคุณสามารถใช้pcregrep :

pcregrep -M '.*\n.*\n.*\n141.299.99.1' file

ตรวจสอบว่าฉันมี PCREGREP หรือไม่ ฉันชอบความกะทัดรัดของคำสั่ง ขอบคุณมากสำหรับเวลาและความพยายามของคุณ ขอขอบคุณ!!!
Dale

4

คุณสามารถใช้วิธีการพื้นฐานแบบเดียวกันกับคำตอบที่ไม่ใช่ grep อื่น ๆ ในเชลล์เอง (ซึ่งถือว่าเชลล์ค่อนข้างล่าสุดที่สนับสนุน=~):

while IFS= read -r line; do 
    [[ $line =~ 141.299.99.1 ]] && printf "%s\n%s\n%s\n%s\n" $a $b $c $line;
    a=$b; b=$c; c=$line; 
done < file 

อีกวิธีหนึ่งคุณสามารถ slurp ไฟล์ทั้งหมดลงในอาร์เรย์:

perl -e '@F=<>; 
        for($i=0;$i<=$#F;$i++){
          print $F[$i-3],$F[$i-2],$F[$i-1],$F[$i] if $F[$i]=~/141.299.99.1/
        }' file 

เปลือกของฉันเก่ามาก - สตีฟจ็อบส์เปิดขั้นตอน ความคิดที่ดีและขอบคุณสำหรับเวลาของคุณ !!! Dale
Dale

@Dale the perl approach จะทำงานได้ทุกที่ โปรดบอกระบบปฏิบัติการของคุณ (เพิ่มในคำถามของคุณ) เพื่อให้เราสามารถแนะนำสิ่งต่าง ๆ ที่เหมาะกับคุณ
terdon

ถ้าฉันคัดลอก Perl ของคุณและวางลงใน NotePad และวางไว้บนบรรทัดเดียวมันใช้งานได้! คำถาม - ถ้าฉันต้องการให้พูด 10 บรรทัดก่อนรูปแบบการแข่งขันที่ฉันจะเปลี่ยน 3 เป็น 10? ขอบคุณ!
Dale

ฉันเห็นว่าฉันสามารถเพิ่มบรรทัดได้อีกโดยเพิ่มคำสั่ง $ F [$ iX] เพิ่มเติม ขอบคุณ!
Dale

4

หากระบบของคุณไม่รองรับgrepบริบทคุณสามารถลองack-grepแทน:

ack -B 3 141.299.99.1 file

ack เป็นเครื่องมืออย่าง grep เหมาะสำหรับโปรแกรมเมอร์


ฉันชอบความกะทัดรัดของคำสั่ง แต่ระบบของฉันไม่สนับสนุน ack ในการค้นหาใน man pages ความคิดที่ดีและขอบคุณมากสำหรับเวลาของคุณ !!! Dale
Dale

@Dale: น่าแปลกใจ! ระบบปฏิบัติการของคุณคืออะไร? ถ้าคุณมีคุณสามารถใช้perl ack
cuonglm

2
awk '/141.299.99.1/{for(i=1;i<=x;)print a[i++];print} {for(i=1;i<x;i++)
     a[i]=a[i+1];a[x]=$0;}'  x=3 filename

ในawkโซลูชันนี้จะใช้อาร์เรย์ซึ่งจะมี 3 บรรทัดก่อนรูปแบบปัจจุบันเสมอ ดังนั้นเมื่อจับคู่รูปแบบเนื้อหาอาร์เรย์พร้อมกับรูปแบบปัจจุบันจะถูกพิมพ์

การทดสอบ

-bash-3.2$ cat filename
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.11
10.0.0.12
10.0.0.13
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1
10.0.0.17
10.0.0.18
10.0.0.19

หลังจากที่ฉันรันคำสั่งเอาต์พุตคือ

10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1

มีรายละเอียดมาก - ขอบคุณมาก ฉันจะลองดู ขอบคุณมากสำหรับเวลาของคุณ !! Dale
Dale

ฉันมีไฟล์ทดสอบและโซลูชันของคุณใช้งานได้! แม้ว่าปัญหาคือเมื่อฉันเรียกใช้ในไฟล์การผลิตขนาดใหญ่ของฉันมันกลับมาพร้อมกับหมายเลขบันทึกยาวเกินไปดังนั้นเอาต์พุตไม่สามารถทำงานกับคำสั่งได้ คำสั่งดั้งเดิมของฉันที่ด้านบนของหน้านี้ใช้งานได้ แต่จะพบเพียงหนึ่งอินสแตนซ์ ฉันขอขอบคุณสำหรับความช่วยเหลือของคุณ มีอะไรที่ฉันสามารถทำได้ด้วยคำสั่งดั้งเดิมของฉันเพื่อให้มันพบมากกว่าหนึ่ง instatnce?
Dale

1

ในส่วนใหญ่เหล่านี้/141.299.99.1/จะจับคู่ (เช่น) 141a299q99+1หรือ141029969951เพราะ.ในนิพจน์ทั่วไปสามารถแสดงถึงอักขระใด ๆ ได้

ใช้/141[.]299[.]99[.]1/มีความปลอดภัยและคุณสามารถเพิ่มบริบทเพิ่มเติมที่จุดเริ่มต้นและจุดสิ้นสุดของ regexp ทั้งหมดเพื่อให้แน่ใจว่ามันไม่ตรงกับ3141., .12, .104ฯลฯ


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