Grep นำบรรทัดที่มีค่า 0 แต่ไม่ใช่ 0.2?


12

ฉันมีไฟล์ที่มีเนื้อหาคล้ายกับไฟล์ต่อไปนี้

0
0
0.2
0
0
0
0

ฉันต้องการลบบรรทัดทั้งหมดด้วยศูนย์เดียว
ฉันคิดว่าจะใช้grep -v "0"แต่สิ่งนี้จะลบบรรทัดที่มี 0.2 ด้วย ฉันเห็นว่าฉันสามารถใช้-wตัวเลือกได้ แต่ดูเหมือนจะไม่ทำงานเช่นกัน

ฉันจะลบบรรทัดทั้งหมดที่มีเพียง 0 เดียวและเก็บบรรทัดเหล่านั้นทั้งหมดที่เริ่มต้นด้วย 0 ได้อย่างไร



1
@JulienLopez ไม่ใช่คำถามที่สงสัยเลย คำถามนั้นเกี่ยวกับการจับคู่คำและตอบด้วย-wซึ่งล้มเหลวที่นี่
Sparhawk

เหตุใดคุณจึงถูกบังคับให้ใช้grepงานนี้ และคุณหมายถึงอะไรโดยศูนย์เดียว ? ฟังดูเหมือนปัญหา XYมาก
Roland Illig

1
@RolandIllig มันเป็นเวลา 1 ชั่วโมงก่อนนอนและฉันต้องการเริ่มต้นการประมวลผลชุด 500,000 สายเพื่อตรวจสอบว่าพวกเขาเป็นกุญแจส่วนตัว bitcoin หรือไม่และได้รับความสมดุล ครั้งต่อไปที่ฉันมีเวลาดูฉันได้ประมวลผลสตริงหลายพันรายการและฉันต้องการแยกวิเคราะห์ค่าที่ไม่เป็นศูนย์
Philip Kirkbride

คำตอบ:


35
grep -vx 0

จากman grep:

-x, --line-regexp
       Select only those matches that exactly match the whole line.
       For a regular expression pattern, this is like parenthesizing
       the pattern and then surrounding it with ^ and $.

-wล้มเหลวเนื่องจากเป็นครั้งแรก0ใน0.02ถือว่าเป็น "คำว่า" และด้วยเหตุนี้สายนี้จะถูกจับคู่ นี่คือเนื่องจากตามด้วยอักขระ "ไม่ใช่คำ" คุณสามารถดูนี้ถ้าคุณเรียกใช้คำสั่งเดิมโดยไม่ต้องคือ-vgrep -w "0"


นอกจากนี้คุณยังสามารถใช้-Fตัวเลือกตั้งแต่ที่เราไม่ได้ใช้รูปแบบ regex เพียงแค่การจับคู่สายธรรมดา
เกล็น Jackman

@glennjackman บางทีฉันอาจจะเคยอ่านมาก่อนหน้านี้ แต่ตอนนี้ฉันหามันไม่เจอ การวิ่งด้วย-F(แปลกใจสำหรับฉัน) ดูเหมือนจะใช้เวลาใกล้เคียงกันหรือช้ากว่าเล็กน้อย (~ 5–10%) ดังนั้นฉันไม่แน่ใจว่าจะได้ประโยชน์อะไร
Sparhawk

2
เป็นไปได้ว่ามีการใช้เอ็นจิ้น RegEx บ่อยและใช้กันอย่างแพร่หลายจนได้ใช้เวอร์ชันที่มีประสิทธิภาพมาก แต่ "การค้นหาธรรมดา" อาจไม่ได้รับการอัพเกรดเป็นเวลา 30 ปี
เนลสัน

@Sparhawk: grepมีกรณีพิเศษสำหรับ regexes ที่ไม่มี metacharacters เพราะนั่นเป็นกรณีการใช้งานทั่วไป มันน่าแปลกใจที่fgrepจะช้าลง แต่ก็ไม่น่าแปลกใจที่ค่าใช้จ่ายในการสังเกตกรณีพิเศษนี้ในขณะที่รวบรวมรูปแบบสั้น ๆ นั้นเล็กน้อยและเวลาในการสแกนไฟล์ขนาดใหญ่ (หากต้องใช้กรณีพิเศษเลยเพื่อให้เร็วไปเทียบกับรูปแบบที่มีคลาสตัวละครหรือx.*y.)
Peter Cordes

แต่นั่นอาจเป็นการทำให้ใหญ่เกินไปเพราะจริงๆแล้วอินพุตนั้นสั้นมาก ๆ (ไม่ใช่หนึ่งสตริงยักษ์) ฉันลืมว่าgrepรู้จักอักขระอื่นใดที่ไม่ใช่\nบรรทัดใหม่เป็นตัวคั่นบรรทัด ถ้าไม่นัย^และยังสามารถเปลี่ยนเป็นค้นหาคงสายเช่น$ strstr(big_buf, "\n0\n")(หรือ0\nที่จุดเริ่มต้นของบัฟเฟอร์) แต่เราไม่เพียงแค่ค้นหาการจับคู่ครั้งแรกที่อาจเป็นบัฟเฟอร์ขนาดใหญ่เราต้องการกรองอย่างมีประสิทธิภาพ แต่ทว่าในทางทฤษฎีแล้วใช่มันเป็นแค่ memcmp ขนาด 2 ไบต์ที่จุดเริ่มต้นของแต่ละบรรทัดและคุณหวังว่าทั้ง fgrep และ grep จะเห็นว่า
Peter Cordes

28

ด้วย grep:

grep -v "^0$" file

^หมายถึงจุดเริ่มต้นของบรรทัด$หมายถึงจุดสิ้นสุดของบรรทัด


2
นี่คือสิ่งที่ผู้ใช้ร้องขอ: หลีกเลี่ยงบรรทัดใด ๆ ที่มีเพียง 1 "0"
Olivier Dulac

1
ฉันจะไม่ใส่เครื่องหมายดอลลาร์ตามตัวอักษรในเครื่องหมายคำพูดคู่นั้น
user541686

@ mehrdad ไม่ได้เป็นปัญหาใหญ่กับ regex เพราะมันมักจะเป็นคนสุดท้ายหรือคนต่อไปจะไม่เป็น[a-Z0-9]
Sampo Sarrala - codidact.org

14

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

  • คุณมีไฟล์ที่มีตัวเลข
  • คุณต้องการดำเนินการกรองขึ้นอยู่กับค่าตัวเลข

Regex ตีความข้อมูลลำดับอักขระ พวกเขาไม่ทราบเกี่ยวกับตัวเลขเพียงเกี่ยวกับตัวเลขแต่ละตัว (และชุดค่าผสมปกติของมัน) แม้ว่าในกรณีของคุณจะมีการแฮ็กแบบง่ายๆเกี่ยวกับข้อ จำกัด นี้ แต่ในที่สุดมันก็ไม่ตรงตามข้อกำหนด

หากไม่มีเหตุผลที่ดีที่จะใช้grepที่นี่ (เช่นเนื่องจากคุณวัดได้และมีประสิทธิภาพมากขึ้นอย่างมากและประสิทธิภาพเป็นสิ่งสำคัญในกรณีของคุณ) ฉันขอแนะนำให้ใช้เครื่องมืออื่น

awkตัวอย่างเช่นสามารถกรองตามการเปรียบเทียบเชิงตัวเลขเช่น:

awk '$1 == 0' your_file

แต่เพื่อรับบรรทัดทั้งหมดที่มีตัวเลขมากกว่าศูนย์:

awk '$1 > 0' your_file

ฉันรัก regex มันเป็นเครื่องมือที่ยอดเยี่ยม แต่มันไม่ใช่เครื่องมือเดียว ถ้าหากคุณมีgrepทุกอย่างดูเหมือนภาษาปกติ


3
ฉันเห็นด้วยอย่างยิ่งว่า awk อาจดูสง่างามกว่าที่นี่ ... อย่างไรก็ตามมันอาจจะตรงกับสิ่งที่ผู้ใช้คาดหวังอีกเล็กน้อย (ทุกค่าตัวเลขประเมินเป็น 0) กล่าวคือprintf '0\n1\n-1\na\nb\n0\n0 also\n0.0\n-0.0\n0*0\n' | awk '($1 == 0)'จะตรงกับ: 0, 0.0และ-0.0... และยัง0 also! ไม่ใช่แค่ "0" (ซึ่งบางครั้งสิ่งที่จำเป็นบางครั้งไม่ได้) หากผู้ใช้ต้องการเพียง "0": awk '/^0$/' (หรือgrep '^0$') นอกจากนี้คุณควรแก้ไข: ผู้ใช้ต้องเพิ่ม!เพื่อคัดค้านการทดสอบดังนั้นจึงซ่อน0(และเลขศูนย์อื่น ๆ ) และแสดงส่วนที่เหลือ เช่น:awk '!( $0 == 0)'
Olivier Dulac

1
@ Olivier หรือตรวจสอบค่าสตริง:$1 == "0"
glenn jackman

1
@OlivierDulac ฉันใช้อย่างชัดเจน>มากกว่า!=(หรือเท่ากัน! (… == …)) เพื่อเน้นว่านี่เป็นการเปรียบเทียบเชิงตัวเลขโดยพลการไม่ใช่แค่ความเท่าเทียมกัน สำหรับความคิดเห็นอื่นของคุณนี่เป็นความจริงโดยสิ้นเชิง แต่เรากลับมาอยู่ในขอบเขตการเปรียบเทียบสตริงและวิธีแก้ปัญหาที่มีอยู่โดยใช้grepผลงาน (แม้ว่าawkแน่นอนยังใช้งานได้)
Konrad Rudolph

@KonradRudolph fair points :)
Olivier Dulac

1
@glennjackman: เคล็ดลับดีแน่นอน แต่จากนั้น OP อยากทำการทดสอบ$0=="0"
Olivier Dulac

5

grep's -wเป็นบิตซับซ้อนในทางที่มันแยกขึ้นสายเดิมเป็น Word และไม่ใช่คำคนละ (อะไรยกเว้นตัวอักษรตัวเลขหรือขีดบริการ) เนื่องจากมันพบแล้วว่าเป็นคำที่ถูกต้อง0อยู่ใน0.02นั้นได้ยืนยันตรรกะการปฏิเสธที่จะลบบรรทัด

การใช้sedเป็นเรื่องง่ายในบริบทนี้เพียงแค่ลบคำทั้งหมดที่ตรงกัน

sed '/^0$/d' file

3

เมื่อเส้นที่คุณต้องการลบเพียง แต่มี0 ตามด้วยบรรทัดถัดไปคุณสามารถเลือกเส้นที่โดยการออกคำสั่งดังต่อไปนี้:

grep -v "^0$"

สิ่งนี้จะพิมพ์การเกิดขึ้นของ0ที่อยู่ท้ายบรรทัดและที่จุดเริ่มต้นของบรรทัดในเวลาเดียวกันเท่านั้น -vตัวเลือกแล้วตีความการเลือกของเรา


1
คำตอบนี้เกือบจะเหมือนกันกับของ Arkadiusz Drabczyk แต่คุณลืมไป-vแล้วดังนั้นมันจึงไม่ได้ผล
Sparhawk

คุณถูก. ฉันกำลังพิมพ์ในขณะที่เขาโพสต์คำตอบของเขาดังนั้นฉันไม่เห็นมันได้รับแล้ว ฉันอ่านส่วนนั้นผิดโดยใช้-vตัวเลือกขอบคุณ!
majesticLSD

0
  • \ b - ขอบคำ

grep -v "\b0\b"

  • ตรงกับจุดเริ่มต้นของบรรทัดรูปแบบและจุดสิ้นสุดของคุณ

grep -v "^0$"

  • หรือเป็น @Sparhawk แนะนำ -vx lineregexp

-w ใช้งานได้ แต่ในกรณีของคุณ0.2มีสองคำเพราะตัวอักษรจุดเป็นตัวคั่นคำ


grep -v "\b0\b"ไม่ได้ทำงานที่นี่ คุณใช้ grep รุ่นใด
Arkadiusz Drabczyk

ทำงานร่วมกับgrep (BSD grep) 2.5.1-FreeBSDบน macOS และgrep (GNU grep) 2.16บน Ubuntu
Jakub Jindra

1
GNU regex ใช้\<และ\>เป็นขอบเขตของคำ แต่จะมีผลเช่นเดียวกับ-w
glenn jackman

0

คำตอบอีกประการหนึ่งเพื่อความหลากหลายโดยสมมติว่าคุณเปิดใช้งาน PCRE grep

grep -Pv "^0(?!\.)"

สิ่งนี้จะทำการมองเชิงลบเพื่อจับคู่บรรทัดที่ขึ้นต้นด้วย0และไม่ได้ตามด้วยจุด จากนั้น-vทิ้งบรรทัดที่ไม่ตรงกัน คุณสามารถเห็นการทำงานได้ที่นี่


1
สิ่งนี้จะลบบรรทัดเช่น0123ซึ่งไม่ใช่สิ่งที่ OP ต้องการ
iruvar

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