วิธีการพิมพ์ทุกบรรทัดหลังจากการจับคู่ถึงจุดสิ้นสุดของไฟล์?


48

อินพุตไฟล์ 1 คือ:

dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

ฉันให้รูปแบบตรงกันจากในother file(เช่นdog 123 4335จาก file2)

ฉันตรงกับรูปแบบของเส้นคือdog 123 4335และหลังจากพิมพ์ทุกบรรทัดโดยไม่ต้องจับคู่บรรทัดผลลัพธ์ของฉันคือ:

cat 13123 23424
deer 2131 213132
bear 2313 21313

หากใช้เฉพาะที่ไม่มีที่อยู่ของบรรทัดใช้รูปแบบเท่านั้นตัวอย่างเช่น1s วิธีจับคู่และพิมพ์บรรทัด?


ไฟล์อื่นสามารถมีรูปแบบเดียวที่จะค้นหาหรือหนึ่งรายการต่อบรรทัดและเริ่มค้นหาที่บรรทัดใดที่จะพบเป็นอันดับแรกในไฟล์ที่ค้นหา
Ciro Santilli 新疆改造中心法轮功六四事件

คำตอบ:


27

สมมติว่าคุณต้องการจับคู่ทั้งบรรทัดกับรูปแบบของคุณกับ GNU sedนี่ใช้งานได้:

sed -n '/^dog 123 4335$/ { :a; n; p; ba; }' infile

เทียบเท่ามาตรฐาน:

sed -ne '/^dog 123 4335$/{:a' -e 'n;p;ba' -e '}' infile

ด้วยอินพุตต่อไปนี้ ( infile):

cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

ผลลัพธ์คือ:

cat 13123 23424 
deer 2131 213132
bear 2313 21313

คำอธิบาย:

  • /^dog 123 4335$/ ค้นหารูปแบบที่ต้องการ
  • :a; n; p; ba;เป็นห่วงว่าจะเรียกบรรทัดใหม่จากการป้อนข้อมูล ( n), พิมพ์มัน ( p) :a; ...; ba;และสาขากลับไปฉลาก

ปรับปรุง

ต่อไปนี้เป็นคำตอบที่ใกล้เคียงกับความต้องการของคุณเช่นรูปแบบใน file2 การ grepping จาก file1:

tail -n +$(( 1 + $(grep -m1 -n -f file2 file1 | cut -d: -f1) )) file1

grep แบบฝังและตัดค้นหาบรรทัดแรกที่มีรูปแบบจาก file2 หมายเลขบรรทัดนี้บวกหนึ่งจะถูกส่งต่อไปยังส่วนท้ายส่วนอีกอันอยู่ที่นั่นเพื่อข้ามบรรทัดที่มีรูปแบบ

หากคุณต้องการเริ่มต้นจากการแข่งขันครั้งสุดท้ายแทนที่จะเป็นนัดแรกมันจะเป็น:

tail -n +$(( 1 + $(grep -n -f file2 file1 | tail -n1 | cut -d: -f1) )) file1

โปรดทราบว่าไม่ใช่ทุกรุ่นของหางสนับสนุนเครื่องหมายบวก


นี่เป็นตัวอย่างแรกของคำสั่ง n และ p ใน sed ที่ฉันได้เห็นว่าไม่รู้สึกอยากนั่งไกลเกินไป ดูเหมือนว่า (จากการทดสอบสั้น ๆ ของฉัน) ที่sed -n '/^dog 123 4335$/ { :a; p; n; ba; }' infile(ด้วยการสลับ p และ n) ประสบความสำเร็จรวมถึงบรรทัดที่ตรงกันเช่นกัน
Josiah Yoder

26

หากคุณมีไฟล์สั้น ๆ อย่างสมเหตุสมผลgrepอาจใช้งานได้:

grep -A5000 -m1 -e 'dog 123 4335' animals.txt

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

grep -A5000 -m1 -e 'dog 123 4335' animals.txt | tail -n+2


หากคุณไม่ต้องการแรก แต่ตรงกับตัวคั่นสุดท้ายคุณสามารถใช้สิ่งนี้:

tac animals.txt | sed -e '/dog 123 4335/q' | tac

บรรทัดนี้อ่านanimals.txtตามลำดับย้อนหลังของบรรทัดและเอาท์พุทจนถึงและรวมถึงบรรทัดที่มีdog 123 4335แล้วย้อนกลับอีกครั้งเพื่อเรียกคืนลำดับที่เหมาะสม

อีกครั้งหากคุณไม่ต้องการผลลัพธ์ในการแข่งขันให้ต่อท้าย (คุณสามารถทำให้นิพจน์ sed ยุ่งเหยิงเพื่อทิ้งบัฟเฟอร์ก่อนที่จะเลิกทำ)


จากการทดสอบของฉัน GNU grep 3.0 ไม่ได้ส่งออกมากกว่า 132 บรรทัดในบริบทหลัง (โดยไม่คำนึงถึงค่าที่ระบุ)
ruvim

22

ในทางปฏิบัติผมอาจจะใช้คำตอบ Aet3miirah ของที่สุดของเวลาและคำตอบ Alexey ของเป็นที่ยอดเยี่ยมเมื่อต้องการที่จะนำทางผ่านเส้น (ยังก็ยังทำงานร่วมกับless) OTOH ฉันชอบแนวทางอื่น (ซึ่งเป็นคำตอบที่ตรงกันข้ามของGilles :

sed -n '/dog 123 4335/,$p'

เมื่อเรียกพร้อมกับ-nแฟล็กsedไม่พิมพ์โดยดีฟอลต์บรรทัดที่ประมวลผลอีกต่อไป จากนั้นเราจะใช้แบบฟอร์ม 2 ที่อยู่ที่ระบุว่าจะใช้คำสั่งจากการจับคู่บรรทัด/dog 123 4335/จนถึงจุดสิ้นสุดของไฟล์ (แสดงโดย$) คำสั่งในคำถามคือpซึ่งพิมพ์บรรทัดปัจจุบัน ดังนั้นนี่หมายถึง "พิมพ์ทุกบรรทัดจากการจับคู่หนึ่ง/dog 123 4335/จนจบ"


3
ที่พิมพ์dogบรรทัดแม้ว่าที่ไม่ต้องการที่นี่
Stéphane Chazelas

1
ดูเหมือนว่าคำตอบที่ดีที่สุด (และใช้ได้กับกรณีของฉันเอง) แต่จะต้องมีการปรับตัวเพื่อข้ามบรรทัดที่ตรงกันเช่นกัน
Pavel Šimerda

1
sed -n '/ dog 123 4335 /, $ p' | sed '1d' จะลบสายสุนัข
Kemin Zhou

1
sed -n '/dog 123 4335/,$p' | tail -n +2จะลบการแข่งขันเช่นกัน
gilad mayani

15
sed -e '1,/dog 123 4335/d' file1

หากคุณต้องการอ่านรูปแบบจากไฟล์ให้เปลี่ยนเป็นคำสั่ง sed หากไฟล์มีรูปแบบ sed:

sed -e "1,/$(cat file2)/d" file1

หากไฟล์มีสตริงตัวอักษรที่ต้องการค้นหาให้อ้างอิงอักขระพิเศษทั้งหมด ฉันถือว่าไฟล์นั้นมีบรรทัดเดียว

sed -e "1,/$(sed 's/[][\\\/^$.*]/\\&/g' file2)/d" file1

^…$หากคุณต้องการการแข่งขันจะเป็นสายทั้งไม่เพียงสตริงย่อยห่อรูปแบบใน

sed -e "1,/^$(sed 's/[][\\\/^$.*]/\\&/g' file2)\$/d" file1

6
สิ่งนี้จะไม่ทำงานหากรูปแบบอยู่ในบรรทัดแรก GNU sedมี0,/dog.../dไว้สำหรับสิ่งนั้น
Stéphane Chazelas

14

$ more +/"dog 123 4335" file1


4
lessนอกจากนี้ยังทำงานร่วมกับ
brandizzi

3
ฉลาดในขั้ว tacแต่ก็ไม่ได้ทำงานจริงถ้าคุณท่อมันกลายเป็นอย่างอื่นเช่น
jcomeau_ictx

ฉันใช้มันแบบนี้ $ more + / "จับคู่คำของฉัน" file1 >> file2
AMB

1
อาจ+ถูกแทนที่ด้วย-pใน POSIX 7: pubs.opengroup.org/onlinepubs/9699919799/utilities/more.htmlแต่ยังไม่ได้ใช้งานใน util-linux 2.20.1 และนี่ยังพิมพ์skipping..และขึ้นบรรทัดใหม่พิเศษ (สำหรับ stderr ที่ฉันคาดหวังดังนั้นอาจใช้ได้)
Ciro Santilli 事件改造中心法轮功六四事件

บางทีสิ่งต่าง ๆ อาจเปลี่ยนแปลงไปนับ แต่นั้นมา? ความคิดเห็นของฉันมี 3 upvotes ดังนั้นมันอาจจะเกี่ยวข้องในเวลา ...
jcomeau_ictx


5

วิธีหนึ่งในการใช้ awk:

awk 'NR==FNR{a[$0];next}f;($0 in a){f=1}'  file2 file1

โดยที่ file2 มีรูปแบบการค้นหาของคุณ ก่อนเนื้อหาทั้งหมดของ file2 จะถูกเก็บไว้ในอาร์เรย์ "a" เมื่อประมวลผล file1 ทุกบรรทัดจะถูกตรวจสอบกับอาร์เรย์และพิมพ์เฉพาะในกรณีที่ไม่มีอยู่


ฉันคิดว่า OP ต้องการส่งออกทุกบรรทัดตามรูปแบบ
Thor

@Thor: ขอบคุณสำหรับการชี้ออกอัปเดตได้ในขณะนี้ ...
คุรุ

ทำได้ดีมาก :)
Thor

5

ถ้าใส่เป็นlseekableแฟ้มปกติ:

ด้วย GNU grep:

{ grep  -xFm1 'dog 123 4335' >&2
  cat; } <infile 2>/dev/null >outfile

ด้วยsed:

{ sed -n '/^dog 123 4335$/q'
  cat; } <infile >outfile

GNU ที่grepเรียกว่า w / -mตัวเลือกจะปิดอินพุตที่การแข่งขัน - และจะปล่อยให้อินพุต(lseekable) fd ทันทีหลังจากจุดที่พบการแข่งขันครั้งสุดท้าย ดังนั้นการเรียกgrepw / -m1ค้นหาการเกิดขึ้นครั้งแรกของรูปแบบในไฟล์และปล่อยออฟเซ็ตอินพุตในตำแหน่งที่ถูกต้องสำหรับcatการเขียนทุกอย่างหลังจากการจับคู่ครั้งแรกของรูปแบบในไฟล์เป็น stdout

แม้ไม่มี GNU grepคุณก็สามารถทำสิ่งเดียวกันกับ w / a POSIX ได้อย่างแม่นยำsed- เมื่อsed qมีการระบุให้ปล่อยออฟเซ็ตอินพุตให้ถูกต้อง sedแม้ว่าGNU จะไม่เป็นไปตามมาตรฐานในลักษณะนี้และดังนั้นข้างต้นอาจไม่ทำงานโดยไม่มี GNU sedเว้นแต่คุณจะเรียกมันด้วย-uสวิตช์


โปรดทราบว่าการsedแบ่งปันสตรีมที่แสดงที่นี่ไม่ได้เป็นพิเศษ (แม้ว่าใช่มาตรฐานที่อ้างถึงนั้นsedเป็นตัวอย่างที่มีประโยชน์อย่างยูทิลิตี้) ของเวิร์กโฟลว์แบบฟรีฟอร์มและแบบมีเงื่อนไขที่แสดง สะดุดตายูทิลิตี้มาตรฐานทั้งหมดมีความหมายและระบุไว้เพื่อให้ความร่วมมือและแบ่งปันตำแหน่งเคอร์เซอร์ของอินพุตสตรีมโดยไม่ทำให้ผู้อ่านรายต่อไปประมวลผลใด ๆ เลย grep -qควรทำสิ่งนี้; grepควรกลับอย่างเงียบ ๆทันทีที่พบการจับคู่อินพุทและอินพุทที่เหลือไม่ควรใช้เป็นค่าเริ่มต้น
mikeserv

4

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

$ cat animals.txt 
cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

GNU sed:

 $ sed '0,/^dog 123 4335$/d' animals.txt 
 cat 13123 23424 
 deer 2131 213132
 bear 2313 21313

Perl:

$ perl -ne 'print unless 1.../^dog 123 4335$/' animals.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Perl ตัวแปรที่มีรูปแบบในไฟล์:

$ cat pattern.txt 
dog 123 4335
$ perl -ne 'BEGIN{chomp($p=(<STDIN>)[0])};print unless 1../$p/;' animals.txt < pattern.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313

2

วันที่ed:

ed -s file1 <<< '/dog 123 4335/+1,$p'

ส่งpคำสั่ง rint หนึ่งคำสั่งไปยัง ed ใน here-string; คำสั่งพิมพ์จะถูก จำกัด ในช่วงหนึ่งหลังจาก ( +1) การdog 123 4335แข่งขันจนถึงจุดสิ้นสุดของไฟล์ ( $)


1

หากคุณไม่สนใจการสร้างไฟล์ชั่วคราวและมีcsplitให้ใช้งานได้ผล:

sh -c 'csplit -sf"$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

หมายเหตุfile1เป็นไฟล์อินพุตและfile2เป็นไฟล์รูปแบบ (ตามที่ระบุในคำถาม)

แบบยาวของคำสั่งข้างต้นคือ:

sh -c 'csplit --quiet --prefix="$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

กล่าวคือ

csplit --quiet --prefix="file1_" "file1" "%^$(cat "file2")%+1" && cat "file1_00"

csplitหากไม่มีprefixแฟล็กข้างต้นจะสร้างไฟล์xx00(ส่วนนำหน้าเป็นxxและส่วนต่อท้าย00) file1_00มีธงข้างต้นนั้นจะสร้างไฟล์ หากไม่มีquietแฟล็กจะพิมพ์ขนาดไฟล์เอาต์พุต (ขนาดของไฟล์ผลลัพธ์)


0

เนื่องจาก awk ไม่ได้รับอนุญาตอย่างชัดแจ้งนี่คือข้อเสนอของฉันโดยถือว่า 'cat' เป็นคู่

awk '$0 ~ /cat/ { vart = NR }{ arr[NR]=$0 } END { for (i = vart; i<=NR ; i++) print arr[i]  }' animals.txt

0

วิธีการพิมพ์ทุกบรรทัดหลังจากการจับคู่ถึงจุดสิ้นสุดของไฟล์?

อีกวิธีที่จะทำให้มันเป็น "วิธีการลบทุกบรรทัดจากที่หนึ่งจนถึงการแข่งขัน (รวมถึง)" และสามารถsedเขียนเป็น:

sed -e '1,/MATCH PATTERN/d'

1
ปัญหาเดียวก็คือเมื่อแบบแผนคือในบรรทัดแรก ...
don_crissti

1
สิ่งนี้แตกต่างจากunix.stackexchange.com/a/56517/32558หรือไม่
Ciro Santilli 事件改造中心法轮功六四事件

ฉันเดาว่าเราต้องการคณะกรรมการที่นี่เพื่อตัดสินใจ
poige

1
@poige: ไม่คุณให้คำตอบเดียวกันน้อยลงทั่ว ๆ ไป
Thor

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