ฉันต้องการค้นหาทุกบรรทัดในไฟล์หลายไฟล์ที่ตรงกับหนึ่งในสองรูปแบบ ฉันพยายามค้นหารูปแบบที่ฉันต้องการโดยพิมพ์
grep (foo|bar) *.txt
แต่เชลล์ตีความ|
ว่าbar
เป็นไพพ์และบ่นเมื่อไม่มีการปฏิบัติการ
ฉันจะ grep สำหรับหลายรูปแบบในไฟล์ชุดเดียวกันได้อย่างไร?
ฉันต้องการค้นหาทุกบรรทัดในไฟล์หลายไฟล์ที่ตรงกับหนึ่งในสองรูปแบบ ฉันพยายามค้นหารูปแบบที่ฉันต้องการโดยพิมพ์
grep (foo|bar) *.txt
แต่เชลล์ตีความ|
ว่าbar
เป็นไพพ์และบ่นเมื่อไม่มีการปฏิบัติการ
ฉันจะ grep สำหรับหลายรูปแบบในไฟล์ชุดเดียวกันได้อย่างไร?
คำตอบ:
ขั้นแรกคุณต้องป้องกันรูปแบบจากการขยายตัวโดยเปลือก วิธีที่ง่ายที่สุดในการทำเช่นนั้นคือใส่เครื่องหมายคำพูดเดี่ยว ๆ เครื่องหมายคำพูดเดี่ยวป้องกันการขยายตัวของสิ่งใด ๆ ระหว่างพวกเขา (รวมถึงแบ็กสแลช); สิ่งเดียวที่คุณทำไม่ได้คือมีเครื่องหมายคำพูดเดี่ยวในรูปแบบ
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
fgrep
หรือgrep -F
สำหรับรูปแบบขนาดเล็กความแตกต่างจะเล็กน้อย แต่เมื่อพวกเขาได้รับอีกต่อไปผลประโยชน์เริ่มแสดง ...
grep -F
มีประโยชน์ด้านประสิทธิภาพที่แท้จริงนั้นขึ้นอยู่กับการใช้ grep หรือไม่: บางตัวใช้อัลกอริทึมเดียวกันอยู่แล้วดังนั้นจึง-F
สร้างความแตกต่างเฉพาะเวลาที่ใช้ในการแยกวิเคราะห์รูปแบบและไม่ค้นหาตามเวลา ตัวอย่าง grep ของ GNU นั้นไม่ได้เร็วขึ้น-F
(เช่นมีข้อบกพร่องที่ทำให้grep -F
ช้าลงในโลแคลหลายไบต์ - รูปแบบคงที่เดียวกันกับที่grep
จริงเร็วกว่ามาก!) ในทางตรงกันข้าม BusyBox grep จะได้รับประโยชน์มากมายจาก-F
ไฟล์ขนาดใหญ่
egrep
มีgrep -E
วันที่ ไม่ใช่เฉพาะ GNU (แน่นอนไม่มีส่วนเกี่ยวข้องกับ Linux) ที่จริงแล้วคุณยังจะได้พบกับระบบเช่น Solaris ที่เริ่มต้นยังไม่สนับสนุนgrep
-E
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 โดยไม่จำเป็นเพราะฉันเรียนรู้จากตัวอย่าง ตอนนี้ฉันเรียนรู้สิ่งใหม่ :)
เช่นเดียวกับ TC1 กล่าวว่า-F
ดูเหมือนจะเป็นตัวเลือกที่ใช้งานได้:
$> cat text
some text
foo
another text
bar
end of file
$> patterns="foo
bar"
$> grep -F "${patterns}" text
foo
bar
ประการแรกคุณต้องใช้เครื่องหมายคำพูดสำหรับอักขระพิเศษ ประการที่สองแม้grep
จะไม่เข้าใจการสลับโดยตรง คุณจะต้องใช้egrep
หรือ (กับ GNU เท่านั้น)grep
grep -E
egrep 'foo|bar' *.txt
(วงเล็บไม่จำเป็นเว้นแต่จะมีการสับเปลี่ยนเป็นส่วนหนึ่งของ regex ที่ใหญ่กว่า)
grep -E
egrep
หากคุณไม่ต้องการนิพจน์ทั่วไปจะใช้งานได้เร็วขึ้นfgrep
หรือgrep -F
มีพารามิเตอร์ -e หลายตัวเช่นนี้
fgrep -efoo -ebar *.txt
fgrep
(หรืออีกทางหนึ่งgrep -F
) นั้นเร็วกว่า grep ปกติมากเพราะมันจะค้นหาสตริงคงที่แทนนิพจน์ทั่วไป
fgrep
เลิกใช้แล้ว
คุณสามารถลองคำสั่งด้านล่างเพื่อรับผล:
egrep 'rose.*lotus|lotus.*rose' some_file
วิธีที่ประหยัดและร่าเริงในการ grep สำหรับหลายรูปแบบ:
$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq
-f
ตัวเลือกของ grep ใช้ไฟล์ที่มีหลายรูปแบบ แทนที่จะสร้างไฟล์ชั่วคราว (ซึ่งคุณอาจลืมที่จะลบหลังจากนั้น) เพียงแค่ใช้การทดแทนกระบวนการของเชลล์:grep -f <(echo foo; echo bar) *.txt
ไพพ์ ( |
) เป็นอักขระเชลล์พิเศษดังนั้นจึงจำเป็นต้องมีการหลบหนี ( \|
) หรืออ้างอิงตามคู่มือ ( 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 ทั้งหมดที่มีอยู่ในไฟล์ได้อย่างไร?
ฉันมีบันทึกการเข้าถึงที่มีการจัดรูปแบบวันที่: [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
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_]* ='
มันใช้งานได้สำหรับฉัน
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#
มีหลายวิธีในการทำเช่นนี้
grep 'foo\|bar' *.txt
egrep 'foo|bar' *.txt
find . -maxdepth 1 -type f -name "*.txt" | xargs grep 'foo\|bar'
find . -maxdepth 1 -type f -name "*.txt" | xargs egrep 'foo|bar'
ตัวเลือกที่ 3 และ 4 จะ grep เฉพาะในไฟล์และหลีกเลี่ยงไดเรกทอรีที่มี.txt
ในชื่อของพวกเขา
ดังนั้นตามกรณีการใช้งานของคุณคุณสามารถใช้ตัวเลือกใด ๆ ที่กล่าวถึงข้างต้น
ขอบคุณ !!
เพื่อเพิ่มคำตอบของ @ geekosaurหากคุณมีหลายรูปแบบที่มีแท็บและพื้นที่คุณใช้คำสั่งดังต่อไปนี้
grep -E "foo[[:blank:]]|bar[[:blank:]]"
โดยที่[[:blank:]]
เป็นคลาสอักขระ RE ที่แทนช่องว่างหรืออักขระแท็บ