grep สามารถแสดงเฉพาะคำที่ตรงกับรูปแบบการค้นหาได้หรือไม่


685

มีวิธีสร้าง grep เอาต์พุต "คำ" จากไฟล์ที่ตรงกับนิพจน์การค้นหาหรือไม่?

หากฉันต้องการค้นหาอินสแตนซ์ทั้งหมดของพูดว่า "th" ในหลาย ๆ ไฟล์ฉันสามารถทำได้:

grep "th" *

แต่ผลลัพธ์จะเป็นสิ่งที่ชอบ (ตัวหนาคือโดยฉัน);

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

สิ่งที่ฉันต้องการให้ผลลัพธ์โดยใช้การค้นหาเดียวกันคือ:

the
the
the
this
thoroughly

สามารถใช้ grep ได้หรือไม่ หรือใช้ชุดเครื่องมืออื่นร่วมกัน?


2
โซลูชัน Dan Midwood ทำงานได้อย่างสมบูรณ์และสมควรได้รับเครดิต
hakish

มีวิธีหนึ่งที่สามารถพิมพ์คำที่ตรงกันเหล่านั้นโดยไม่ต้องเปลี่ยนบรรทัด สตริงที่ตรงกันควรอยู่ในบรรทัดเดียวกันหรือไม่
นักภาษาศาสตร์

คำตอบ:


956

ลอง grep -o

grep -oh "\w*th\w*" *

แก้ไข: การจับคู่จากความคิดเห็นของ Phil

จากเอกสาร :

-h, --no-filename
    Suppress the prefixing of file names on output. This is the default
    when there is only  one  file  (or only standard input) to search.
-o, --only-matching
    Print  only  the matched (non-empty) parts of a matching line,
    with each such part on a separate output line.

9
@ user181548, ตัวเลือก grep -o ใช้ได้กับ grep GNU เท่านั้น ดังนั้นหากคุณไม่ได้ใช้ GNU grep มันอาจไม่เหมาะกับคุณ
ksinkar

5
@ABB มันขึ้นอยู่กับว่าคุณต้องการแสดงชื่อของไฟล์ที่ตรงกันหรือไม่ ฉันไม่แน่ใจภายใต้เงื่อนไขว่ามันทำอะไรและไม่แสดง แต่ฉันรู้ว่าเมื่อฉันใช้ grep ในหลาย ๆ ไดเรกทอรีมันแสดงเส้นทางไฟล์แบบเต็มสำหรับไฟล์ที่ตรงกันทั้งหมดในขณะที่ -h มันเพิ่งแสดง คำที่ตรงกันโดยไม่มีข้อกำหนดเกี่ยวกับไฟล์ใด ๆ ดังนั้นเพื่อให้ตรงกับคำถามเดิมฉันคิดว่ามันจำเป็นในบางสถานการณ์
LokMac

1
ฉันต้องการคำอธิบายสำหรับความ"\w*th\w*" *หมายดังนั้นฉันคิดว่าฉันโพสต์ \wคือ [_ [: alnum:]] ดังนั้นสิ่งนี้จะจับคู่ "word" ใด ๆ ที่มี 'th' (เนื่องจาก\wไม่มีช่องว่าง) * หลังส่วนที่ยกมาคือ glob สำหรับไฟล์ใด (เช่นการจับคู่ไฟล์ทั้งหมดในไดเรกทอรีนี้)
jeremysprofile

1
\wคือไม่ได้โดยทั่วไปแบบพกพาgrep -E; เพื่อความสะดวกในการพกพาให้ใช้ชื่อคลาสอักขระ POSIX [[:alnum:]]แทน (หรือ[_[:alnum:]]ถ้าคุณต้องการขีดเส้นใต้หรือgrep -Pถ้าลองใช้แพลตฟอร์มของคุณ)
tripleee

@ABB จากการแสดงผลที่ต้องการที่แสดงโดย OP -hสิ่งที่ฉันจำเป็นต้องบอกคือ .. ?
El Ronnoco

81

คำตอบที่ปลอดภัยข้ามการกระจาย (รวม windows minGW?)

grep -h "[[:alpha:]]*th[[:alpha:]]*" 'filename' | tr ' ' '\n' | grep -h "[[:alpha:]]*th[[:alpha:]]*"

หากคุณใช้ grep เวอร์ชันเก่า (เช่น 2.4.2) ซึ่งไม่รวมถึงตัวเลือก -o ใช้ข้างต้น อื่นใช้ง่ายกว่าในการรักษารุ่นด้านล่าง

Linux การกระจายข้ามคำตอบที่ปลอดภัย

grep -oh "[[:alpha:]]*th[[:alpha:]]*" 'filename'

เพื่อสรุป-ohผลลัพธ์นิพจน์ทั่วไปที่ตรงกับเนื้อหาไฟล์ (ไม่ใช่ชื่อไฟล์) เช่นเดียวกับที่คุณคาดหวังว่านิพจน์ทั่วไปจะทำงานใน vim / etc ... คำหรือนิพจน์ทั่วไปที่คุณต้องการค้นหานั้นขึ้นอยู่กับ คุณ! ตราบใดที่คุณยังคง POSIX และไม่ใช้ไวยากรณ์ (ดูด้านล่าง)

เพิ่มเติมจากคู่มือสำหรับ grep

-o      Print each match, but only the match, not the entire line.
-h      Never print filename headers (i.e. filenames) with output lines.
-w      The expression is searched for as a word (as if surrounded by
         `[[:<:]]' and `[[:>:]]';

สาเหตุที่คำตอบดั้งเดิมไม่ได้ผลสำหรับทุกคน

การใช้\wแตกต่างกันไปในแต่ละแพลตฟอร์มเนื่องจากเป็นไวยากรณ์ "perl" แบบขยาย เช่นผู้ที่ติดตั้ง grep ที่มีข้อ จำกัด ในการทำงานกับ POSIX ชั้นเรียนตัวอักษรการใช้งาน[[:alpha:]]และไม่เทียบเท่า Perl \wของ ดูหน้า Wikipedia เกี่ยวกับการแสดงออกปกติสำหรับข้อมูลเพิ่มเติม

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

สำหรับการสนับสนุน grep ที่ไม่มีตัวเลือก -o grep แรกจะส่งออกบรรทัดที่เกี่ยวข้อง tr จะแยกช่องว่างกับบรรทัดใหม่ตัวกรอง grep สุดท้ายสำหรับบรรทัดที่เกี่ยวข้องเท่านั้น

(PS: ฉันรู้ว่าแพลตฟอร์มส่วนใหญ่ในตอนนี้จะได้รับการติดตั้งสำหรับ \ w .... แต่มีที่ล้าหลังอยู่เสมอ)

เครดิตสำหรับวิธีแก้ปัญหา "-o" จากคำตอบ @AdamRosenfield


1
สิ่งที่เกี่ยวกับ -o ทำงานใน grep GNU เท่านั้น (ดังที่ ksinkar พูดถึงในความคิดเห็นของคำตอบที่ยอมรับแล้ว)
Brilliand

@ Brilliand อืมฉันมีปัญหาในการค้นหาการใช้งานลินุกซ์ที่ไม่รองรับ '-o' ฉันสามารถหางานทำถ้าฉันรู้ว่าจะตรวจสอบกับแพลตฟอร์มใด
PicoCreator

@pico ไม่มี-oตัวเลือกใน grep windows ที่ติดตั้งด้วยแพ็คเกจ git (minGW?): "c:\Program Files (x86)\Git\bin\grep" --version grep (GNU grep) 2.4.2
Bruce Peterson

@ BrucePeterson ฉันได้เพิ่มใน AdamRosenfield วิธีแก้ปัญหาสำหรับ -o: ช่วยฉันตรวจสอบว่า windows git รวม tr / sed และรุ่นของมัน ดังนั้นฉันสามารถตรวจสอบว่าวิธีแก้ปัญหานี้ใช้งานได้
PicoCreator

@pico: สำหรับ GIT: GNU sed เวอร์ชั่น 4.2.1, tr (GNU textutils) 2.0
Bruce Peterson

46

ง่ายกว่าที่คุณคิด ลองสิ่งนี้:

egrep -wo 'th.[a-z]*' filename.txt #### (Case Sensitive)

egrep -iwo 'th.[a-z]*' filename.txt  ### (Case Insensitive)

ที่ไหน

 egrep: Grep will work with extended regular expression.
 w    : Matches only word/words instead of substring.
 o    : Display only matched pattern instead of whole line.
 i    : If u want to ignore case sensitivity.

2
ดูเหมือนจะไม่เพิ่มอะไรเลยในคำตอบที่มีอยู่จาก 4+ ปีก่อน
tripleee

3
@tripleee ฉันพบว่าวิธีการของฉันดีกว่าและเรียบง่ายดังนั้นฉันจึงโพสต์สิ่งนี้
Abhinandan prasad

42

คุณสามารถแปลช่องว่างเป็นบรรทัดใหม่แล้ว grep เช่น:

cat * | tr ' ' '\n' | grep th

18
ไม่ต้องการแมว tr '' '\ n' <ไฟล์ | grep th ช้าสำหรับไฟล์ขนาดใหญ่
ghostdog74

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

@ ghostdog74: จุดดีแม้ว่าคุณจะมีมากกว่าไฟล์คุณจะต้องใช้ cat @ Neil Baldwin: คุณแน่ใจหรือว่าพิมพ์ถูกต้อง? เมื่อมีไฟล์อินพุตเพียงไฟล์เดียว (stdin ในกรณีนี้) grep จะไม่พิมพ์ชื่อไฟล์
Adam Rosenfield

@Adam - ใช่อดัมเสียใจมันทำงานกับไฟล์เดียว แต่ไม่ได้หลายไฟล์
Neil Baldwin

4
@ ghostdog74 หากส่วนที่ช้านั้นเป็นเพราะtrเขาสามารถทำได้grepก่อนดังนั้นtrจะถูกนำไปใช้กับเส้นที่ตรงกันเท่านั้น:grep th filename | tr ' ' '\n' | grep th
Carcamano

37

เพียงแค่awkไม่จำเป็นต้องใช้เครื่องมือร่วมกัน

# awk '{for(i=1;i<=NF;i++){if($i~/^th/){print $i}}}' file
the
the
the
this
thoroughly

8
@AjeetGanga ดีมันอยู่ในชื่อ
Daerdemandt

11

คำสั่ง grep สำหรับการจับคู่และ Perl เท่านั้น

grep -o -P 'th.*? ' filename

3
สิ่งที่เกี่ยวกับการแสดงเฉพาะกลุ่มที่ตรงกันหรือไม่
Bishwas Mishra

มันใช้งานไม่ได้ จะพบเพียงthเพราะคุณขอให้ใช้สัญลักษณ์ตัวแทนซ้ำที่สั้นที่สุด
tripleee

@tripleee - มันจะไม่มีปัญหานั้นเพราะมีพื้นที่รวมอยู่ในตอนท้ายของ regex อย่างไรก็ตามมันจะพลาดคำที่ไม่มีช่องว่างหลังจากพวกมันเช่นที่ส่วนท้ายของบรรทัด
Ken Williams

8

ฉันไม่พอใจกับ awk ที่ยากต่อการจดจำไวยากรณ์ แต่ฉันชอบความคิดในการใช้ยูทิลิตี้เดียวในการทำสิ่งนี้

ดูเหมือนว่า ack (หรือ ack-grep ถ้าคุณใช้ Ubuntu) สามารถทำได้อย่างง่ายดาย:

# ack-grep -ho "\bth.*?\b" *

the
the
the
this
thoroughly

หากคุณไม่ใช้แฟล็ก -h คุณจะได้รับ:

# ack-grep -o "\bth.*?\b" *

some-other-text-file
1:the

some-text-file
1:the
the

yet-another-text-file
1:this
thoroughly

ในฐานะโบนัสคุณสามารถใช้การ--outputตั้งค่าสถานะเพื่อทำสิ่งนี้เพื่อการค้นหาที่ซับซ้อนยิ่งขึ้นด้วยไวยากรณ์ที่ง่ายที่สุดที่ฉันได้พบ:

# echo "bug: 1, id: 5, time: 12/27/2010" > test-file
# ack-grep -ho "bug: (\d*), id: (\d*), time: (.*)" --output '$1, $2, $3' test-file

1, 5, 12/27/2010


4

เพื่อค้นหาคำทั้งหมดที่ขึ้นต้นด้วย "ไอคอน -" คำสั่งต่อไปนี้ใช้งานได้สมบูรณ์ ฉันใช้Ackที่นี่ซึ่งคล้ายกับ grep แต่มีตัวเลือกที่ดีกว่าและการจัดรูปแบบที่ดี

ack -oh --type=html "\w*icon-\w*" | sort | uniq

3

นอกจากนี้คุณยังสามารถลองpcregrep นอกจากนี้ยังมี-wตัวเลือกในgrepแต่ในบางกรณีมันไม่ทำงานตามที่คาดไว้

จากWikipedia :

cat fruitlist.txt
apple
apples
pineapple
apple-
apple-fruit
fruit-apple

grep -w apple fruitlist.txt
apple
apple-
apple-fruit
fruit-apple

3

ฉันมีปัญหาที่คล้ายกันมองหา grep / pattern regex และ "pattern pattern ที่พบ" เป็นผลลัพธ์

ในตอนท้ายฉันใช้ egrep (regex เดียวกันบน grep -e หรือ -G ไม่ได้ให้ผลลัพธ์เดียวกันกับ egrep) ด้วยตัวเลือก -o

ดังนั้นฉันคิดว่าอาจเป็นสิ่งที่คล้ายกับ (ฉันไม่ใช่อาจารย์ regex):

egrep -o "the*|this{1}|thoroughly{1}" filename

{1}ปริมาณที่ไร้ประโยชน์ควรจะลดลง หรือถ้าคุณต้องการให้สอดคล้องt{1}h{1}e{1}ฯลฯ
tripleee

สามารถพิมพ์ด้วยบรรทัดเดียวกันได้หรือไม่?
吴毅凡

-1

คุณสามารถไพพ์เอาต์พุต grep ของคุณลงใน Perl ดังนี้:

grep "th" * | perl -n -e'while(/(\w*th\w*)/g) {print "$1\n"}'

9
ที่จะไม่ให้ผลลัพธ์ที่ถูกต้อง ถ้าใช้ Perl ก็ไม่จำเป็นต้องใช้ grep ทำทุกอย่างใน Perl
ghostdog74

ขอบคุณที่ชี้ข้อผิดพลาด ghostdog74 ฉันเปลี่ยนมันเพื่อพิมพ์คำทั้งหมดในบรรทัดไม่ใช่แค่คำแรก

อย่างที่ฉันพูด grep ไม่จำเป็น perl -n -e'while (/ (\ s + th \ w *) / g) {พิมพ์ไฟล์ "$ 1 \ n"} '
ghostdog74

7
แล้วแต่คุณ. ฉันแค่อธิบายจุด หากไม่จำเป็นก็ไม่ควรทำ นั่นพิเศษ "|" จะทำให้คุณเสียค่าใช้จ่ายมากกว่าหนึ่งขั้นตอน
ghostdog74

1
ใน Perl 5.10 หรือใหม่กว่า: perl -nE '@a = / (regexp) / ig; พูดเข้าร่วม "\ n", @a '
ศาสตราจารย์โฟตอน

-1
$ grep -w

ตัดตอนมาจากหน้าคน grep:

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


1
ที่จะยังคงพิมพ์ทั้งบรรทัดที่มีการแข่งขัน มัน จำกัด การแข่งขันจริงเพื่อtheไม่ให้ตรงเช่น "เหล่านี้" หรือ "อาบน้ำ"
tripleee

-6

ripgrep

นี่คือตัวอย่างการใช้ripgrep:

rg -o "(\w+)?th(\w+)?"

thมันจะตรงกับคำที่ตรงกันทั้งหมด

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