ฉันจะ grep สำหรับรูปแบบหลายรูปแบบโดยมีรูปแบบที่มีอักขระไพพ์ได้อย่างไร


624

ฉันต้องการค้นหาทุกบรรทัดในไฟล์หลายไฟล์ที่ตรงกับหนึ่งในสองรูปแบบ ฉันพยายามค้นหารูปแบบที่ฉันต้องการโดยพิมพ์

grep (foo|bar) *.txt

แต่เชลล์ตีความ|ว่าbarเป็นไพพ์และบ่นเมื่อไม่มีการปฏิบัติการ

ฉันจะ grep สำหรับหลายรูปแบบในไฟล์ชุดเดียวกันได้อย่างไร?


เป็นไปได้ที่ซ้ำกันของGrep: วิธีการเพิ่มเงื่อนไข“ หรือ”?
phuclv

grep 'word1 \ | word2 \ | word3' / path / to / file
lambodar

คำตอบ:


861

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

grep 'foo*' *.txt

หากคุณต้องการอัญประกาศเดี่ยวคุณสามารถเขียนมันเป็น'\''(ตัวอักษรสตริงสิ้นสุดตัวอักษรคำพูดตัวอักษรตัวอักษรเปิดสตริง)

grep 'foo*'\''bar' *.txt

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

grep 'foo\|bar' *.txt

วิธีแบบพกพาคือการใช้ไวยากรณ์ใหม่ขยายการแสดงออกปกติ คุณต้องผ่าน-Eตัวเลือกgrepเพื่อเลือก บน Linux คุณสามารถพิมพ์egrepแทนgrep -E(บน unices อื่นคุณสามารถสร้างนามแฝงได้)

grep -E 'foo|bar' *.txt

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

grep -e foo -e bar *.txt

18
ในฐานะที่เป็น sidenote - เมื่อรูปแบบได้รับการแก้ไขแล้วคุณควรจะได้รับนิสัยfgrepหรือgrep -Fสำหรับรูปแบบขนาดเล็กความแตกต่างจะเล็กน้อย แต่เมื่อพวกเขาได้รับอีกต่อไปผลประโยชน์เริ่มแสดง ...
TC1

7
@ TC1 fgrep เลิกใช้แล้วตามหน้า man
ramn

18
@ TC1 การgrep -Fมีประโยชน์ด้านประสิทธิภาพที่แท้จริงนั้นขึ้นอยู่กับการใช้ grep หรือไม่: บางตัวใช้อัลกอริทึมเดียวกันอยู่แล้วดังนั้นจึง-Fสร้างความแตกต่างเฉพาะเวลาที่ใช้ในการแยกวิเคราะห์รูปแบบและไม่ค้นหาตามเวลา ตัวอย่าง grep ของ GNU นั้นไม่ได้เร็วขึ้น-F(เช่นมีข้อบกพร่องที่ทำให้grep -Fช้าลงในโลแคลหลายไบต์ - รูปแบบคงที่เดียวกันกับที่grepจริงเร็วกว่ามาก!) ในทางตรงกันข้าม BusyBox grep จะได้รับประโยชน์มากมายจาก-Fไฟล์ขนาดใหญ่
Gilles

4
บางทีมันควรจะกล่าวถึงว่าสำหรับรูปแบบที่ซับซ้อนมากขึ้นซึ่งการสลับเป็นเพียงเพื่อเป็นส่วนหนึ่งของการแสดงออกปกติก็สามารถจัดกลุ่มกับ "\ (" และ "\)" (การหลบหนีเป็นสำหรับการเริ่มต้น "นิพจน์ปกติพื้นฐาน" ) (?)
Peter Mortensen

4
โปรดทราบว่าegrepมีgrep -Eวันที่ ไม่ใช่เฉพาะ GNU (แน่นอนไม่มีส่วนเกี่ยวข้องกับ Linux) ที่จริงแล้วคุณยังจะได้พบกับระบบเช่น Solaris ที่เริ่มต้นยังไม่สนับสนุนgrep -E
Stéphane Chazelas

90
egrep "foo|bar" *.txt

หรือ

grep "foo\|bar" *.txt
grep -E "foo|bar" *.txt

เลือกอ้างถึงหน้าคนของ gnu-grep:

   -E, --extended-regexp
          Interpret PATTERN as an extended regular expression (ERE, see below).  (-E is specified by POSIX.)

Matching Control
   -e PATTERN, --regexp=PATTERN
          Use PATTERN as the pattern.  This can be used to specify multiple search patterns, or to protect  a  pattern
          beginning with a hyphen (-).  (-e is specified by POSIX.)

( ... )

   grep understands two different versions of regular expression syntax: basic and extended.”  In  GNU grep,  there
   is  no  difference  in  available  functionality  using  either  syntax.   In  other implementations, basic regular
   expressions are less powerful.  The following description applies to extended regular expressions; differences  for
   basic regular expressions are summarized afterwards.

ในตอนแรกฉันไม่ได้อ่านเพิ่มเติมดังนั้นฉันไม่รู้จักความแตกต่างที่ลึกซึ้ง:

Basic vs Extended Regular Expressions
   In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead  use  the
   backslashed versions \?, \+, \{, \|, \(, and \).

ฉันมักจะใช้ egrep และ parens โดยไม่จำเป็นเพราะฉันเรียนรู้จากตัวอย่าง ตอนนี้ฉันเรียนรู้สิ่งใหม่ :)


22

เช่นเดียวกับ TC1 กล่าวว่า-Fดูเหมือนจะเป็นตัวเลือกที่ใช้งานได้:

$> cat text
some text
foo
another text
bar
end of file

$> patterns="foo
bar" 

$> grep -F "${patterns}" text
foo
bar

1
@poige ฉันไม่ทราบเกี่ยวกับตัวเลือก $ 'foo \ nbar' ไม่แน่ใจว่าส่วนขยายทำงานที่นี่ต้องค้นหาอย่างไร แต่ขอขอบคุณว่ามันมีประโยชน์จริงๆ
haridsv

ดี! ตัวเลือกนี้ดูเหมือนว่าจะทำให้มันทำงานได้เร็วขึ้นมาก (เนื่องจากปิดการใช้งาน regex)
qwertzguy

15

ประการแรกคุณต้องใช้เครื่องหมายคำพูดสำหรับอักขระพิเศษ ประการที่สองแม้grepจะไม่เข้าใจการสลับโดยตรง คุณจะต้องใช้egrepหรือ (กับ GNU เท่านั้น)grepgrep -E

egrep 'foo|bar' *.txt

(วงเล็บไม่จำเป็นเว้นแต่จะมีการสับเปลี่ยนเป็นส่วนหนึ่งของ regex ที่ใหญ่กว่า)


4
อันที่จริงเป็นมาตรฐานมากกว่าgrep -E egrep
jw013

8

หากคุณไม่ต้องการนิพจน์ทั่วไปจะใช้งานได้เร็วขึ้นfgrepหรือgrep -Fมีพารามิเตอร์ -e หลายตัวเช่นนี้

fgrep -efoo -ebar *.txt

fgrep(หรืออีกทางหนึ่งgrep -F) นั้นเร็วกว่า grep ปกติมากเพราะมันจะค้นหาสตริงคงที่แทนนิพจน์ทั่วไป


4
โปรดดูความคิดเห็นในหน้านี้ที่กล่าวถึงซึ่งfgrepเลิกใช้แล้ว
phk


3

วิธีที่ประหยัดและร่าเริงในการ grep สำหรับหลายรูปแบบ:

$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq

มันอาจได้ประโยชน์จากคำอธิบาย
Peter Mortensen

2
คำอธิบายคือ-fตัวเลือกของ grep ใช้ไฟล์ที่มีหลายรูปแบบ แทนที่จะสร้างไฟล์ชั่วคราว (ซึ่งคุณอาจลืมที่จะลบหลังจากนั้น) เพียงแค่ใช้การทดแทนกระบวนการของเชลล์:grep -f <(echo foo; echo bar) *.txt
23718 Jakob

3

ไพพ์ ( |) เป็นอักขระเชลล์พิเศษดังนั้นจึงจำเป็นต้องมีการหลบหนี ( \|) หรืออ้างอิงตามคู่มือ ( man bash):

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

การใส่อักขระในเครื่องหมายคำพูดคู่จะเก็บรักษาค่าตัวอักษรของอักขระทั้งหมดภายในเครื่องหมายคำพูด

เครื่องหมายแบ็กสแลชที่ไม่ใช่เครื่องหมายอัญประกาศ ( \) เป็นอักขระยกเว้น

ดู: ตัวละครใดที่ต้องหลบหนีใน Bash?

นี่คือตัวอย่างบางส่วน (การใช้เครื่องมือที่ยังไม่ได้กล่าวถึง):

  • การใช้ripgrep:

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • การใช้git grep:

    • git grep --no-index -e foo --or -e bar

      หมายเหตุ: นอกจากนี้ยังสนับสนุนการแสดงออกบูลีนเช่น--and, และ--or--not

สำหรับและการดำเนินการต่อบรรทัดดู: วิธีการรัน grep ที่มีหลายรูปแบบและ?

สำหรับ AND operation ต่อไฟล์โปรดดู: วิธีตรวจสอบสตริงหรือ regexes ทั้งหมดที่มีอยู่ในไฟล์ได้อย่างไร?


3

ฉันมีบันทึกการเข้าถึงที่มีการจัดรูปแบบวันที่: [30 / Jun / 2013: 08: 00: 45 +0200]

แต่ฉันต้องการแสดงเป็น: 30 / Jun / 2013 08:00:45

ปัญหาคือการใช้ "OR" ในคำสั่ง grep ของฉันฉันได้รับนิพจน์การจับคู่สองรายการในสองบรรทัดแยกกัน

นี่คือทางออก:

grep -in myURL_of_interest  *access.log  | \
grep -Eo '(\b[[:digit:]]{2}/[[:upper:]][[:lower:]]{2}/[[:digit:]]{4}|[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\b)'   \
| paste - - -d" " > MyAccess.log

2

TL; DR: ถ้าคุณต้องการทำสิ่งต่าง ๆ มากขึ้นหลังจากจับคู่หนึ่งในหลายรูปแบบ \(pattern1\|pattern2\)

ตัวอย่าง: ฉันต้องการค้นหาสถานที่ทั้งหมดที่ตัวแปรที่มีชื่อ 'date' ถูกกำหนดเป็น String หรือ int (เช่น "int cronDate =" หรือ "String textFormattedDateStamp ="):

cat myfile | grep '\(int\|String\) [a-zA-Z_]*date[a-zA-Z_]* =' 

ด้วยgrep -Eคุณไม่จำเป็นต้องหลบเลี่ยงวงเล็บหรือไปป์นั่นคือgrep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='


1

มันใช้งานได้สำหรับฉัน

root@gateway:/home/sshuser# aws ec2 describe-instances --instance-ids i-2db0459d |grep 'STATE\|TAG'

**STATE**   80      stopped

**STATE**REASON     Client.UserInitiatedShutdown    Client.UserInitiatedShutdown: User initiated shutdown

**TAGS**    Name    Magento-Testing root@gateway:/home/sshuser#

1

มีหลายวิธีในการทำเช่นนี้

  1. grep 'foo\|bar' *.txt
  2. egrep 'foo|bar' *.txt
  3. find . -maxdepth 1 -type f -name "*.txt" | xargs grep 'foo\|bar'
  4. find . -maxdepth 1 -type f -name "*.txt" | xargs egrep 'foo|bar'

ตัวเลือกที่ 3 และ 4 จะ grep เฉพาะในไฟล์และหลีกเลี่ยงไดเรกทอรีที่มี.txtในชื่อของพวกเขา
ดังนั้นตามกรณีการใช้งานของคุณคุณสามารถใช้ตัวเลือกใด ๆ ที่กล่าวถึงข้างต้น
ขอบคุณ !!


0

เพื่อเพิ่มคำตอบของ @ geekosaurหากคุณมีหลายรูปแบบที่มีแท็บและพื้นที่คุณใช้คำสั่งดังต่อไปนี้

grep -E "foo[[:blank:]]|bar[[:blank:]]"

โดยที่[[:blank:]]เป็นคลาสอักขระ RE ที่แทนช่องว่างหรืออักขระแท็บ

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