grep ค้นหาคำสองคำในหนึ่งบรรทัด


45

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


1
ในการค้นหาสตริงทั้งหมดในไฟล์คุณสามารถรัน grep ใน FOR วนวน: unix.stackexchange.com/a/462445/43233
Noam Manos

คำตอบ:


61

"ทั้งคู่ในบรรทัดเดียวกัน" หมายถึง "'ข้าว' ตามด้วยตัวอักษรสุ่มตามด้วย 'มะนาว' หรือวิธีอื่น ๆ '

ใน regex ที่เป็นหรือrice.*lemon lemon.*riceคุณสามารถรวมที่ใช้|:

grep -E 'rice.*lemon|lemon.*rice' some_file

หากคุณต้องการใช้ regex ปกติแทนที่จะเป็นส่วนขยาย ( -E) คุณต้องมีแบ็กสแลชก่อน|:

grep 'rice.*lemon\|lemon.*rice' some_file

สำหรับคำที่ยาวขึ้นอย่างรวดเร็วและมักจะใช้งานได้ง่ายขึ้นหลายสายgrepเช่น:

grep rice some_file | grep lemon | grep chicken

บรรทัดสุดท้ายของคุณคือการรวมกันไม่แยกไม่? เพื่อปัญญาที่: พบบรรทัดที่มีgrep rice riceมันถูกป้อนเข้าgrep lemonซึ่งจะพบเพียงบรรทัดที่มีมะนาว .. และอื่น ๆ ในขณะที่ OP - เช่นเดียวกับคำตอบของคุณก่อน - จะช่วยให้การใด ๆของ [ข้าว | มะนาว | ไก่]
javadba

เวอร์ชันของสคริปต์: askubuntu.com/a/879253/5696
Jeff

@Florian Diesch - มายด์อธิบายว่าทำไม|จะต้องหนีgrep? ขอบคุณ!
ผู้ลี้ภัย

1
@fugitive egrepใช้ Extended regex ที่|เข้าใจว่าเป็นตรรกะหรือ grepเริ่มต้นที่ regex ขั้นพื้นฐานซึ่ง\|เป็น OR
Sergiy Kolodyazhnyy

ตามที่ระบุในgrepmanpage 's, เลิกและควรถูกแทนที่ด้วยegrep grep -Eฉันใช้เสรีภาพในการแก้ไขคำตอบตามนั้น
ของหวาน

26

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

grep <first_pattern> <file_name> | grep <second_pattern>

หรือ,

cat <file_name> | grep <first_pattern> | grep <second_pattern>

ตัวอย่าง:

เพิ่มเนื้อหาลงในไฟล์ของเรา:

$ echo "This line contains lemon." > test_grep.txt
$ echo "This line contains rice." >> test_grep.txt
$ echo "This line contains both lemon and rice." >> test_grep.txt
$ echo "This line doesn't contain any of them." >> test_grep.txt
$ echo "This line also contains both rice and lemon." >> test_grep.txt

ไฟล์ประกอบด้วยอะไรบ้าง:

$ cat test_grep.txt 
This line contains lemon.
This line contains rice.
This line contains both lemon and rice.
This line doesn't contain any of them.
This line also contains both rice and lemon.

ตอนนี้ลอง grep สิ่งที่เราต้องการ:

$ grep rice test_grep.txt | grep lemon
This line contains both lemon and rice.
This line also contains both rice and lemon.

เรารับเฉพาะเส้นที่ทั้งสองรูปแบบตรงกันเท่านั้น คุณสามารถขยายสิ่งนี้และไพพ์เอาต์พุตไปยังคำสั่ง grep อื่นสำหรับการจับคู่ "AND" เพิ่มเติม


21

แม้ว่าคำถามจะถามถึง 'grep' แต่ฉันคิดว่าอาจเป็นประโยชน์ในการโพสต์วิธีแก้ปัญหา 'awk' ที่เรียบง่าย:

awk '/lemon/ && /rice/'

สิ่งนี้สามารถขยายได้อย่างง่ายดายด้วยคำที่มากขึ้นหรือการแสดงออกแบบบูลอื่น ๆ นอกเหนือจาก 'และ'


11

แนวคิดอื่นในการค้นหาการแข่งขันในลำดับใด ๆ คือการใช้:

grep พร้อมตัวเลือก-P (Perl-Compatibility)และregex lookahead เชิงบวก(?=(regex)) :

grep -P '(?=.*?lemon)(?=.*?rice)' infile

หรือคุณสามารถใช้ด้านล่างแทน:

grep -P '(?=.*?rice)(?=.*?lemon)' infile
  • .*?วิธีการที่ตรงกับตัวอักษรใด ๆ.ที่เกิดขึ้นเป็นศูนย์ครั้งหรือมากกว่า*ในขณะที่พวกเขาเป็นตัวเลือกตามรูปแบบ ( riceหรือlemon) ?ทำให้ไม่จำเป็นทุกอย่างก่อนที่มัน (หมายถึงการเป็นศูนย์หรือเวลาหนึ่งทุกอย่างจับคู่.*)

(?=pattern): Positive Lookahead: โครงสร้าง Lookahead เชิงบวกคือวงเล็บหนึ่งคู่โดยมีวงเล็บเปิดตามด้วยเครื่องหมายคำถามและเครื่องหมายเท่ากับ

ดังนั้นสิ่งนี้จะส่งคืนบรรทัดทั้งหมดที่มีทั้งแบบสุ่มlemonและriceแบบสุ่ม นอกจากนี้จะหลีกเลี่ยงการใช้|และสองเท่าgreps


ลิงก์ภายนอก: หัวข้อ Grep ขั้นสูงLookahead เชิงบวก - GREP สำหรับนักออกแบบ



1

หากเรายอมรับว่าการให้คำตอบที่ไม่ได้grepใช้นั้นเป็นที่ยอมรับเช่นเดียวกับคำตอบข้างต้นawkฉันก็ขอเสนอperlบรรทัดง่ายๆเช่น:

$ perl -ne 'print if /lemon/ and /rice/' my_text_file

การค้นหาสามารถละเว้นตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ / บางคำ/lemon/i and /rice/iทั้งหมด ในเครื่องส่วนใหญ่ของ Unix / Linux นั้นมีการติดตั้งรวมทั้ง awk อยู่ดี


ปฏิเสธ!!! ;) เพราะมันทำให้รู้สึกไม่ .. :)
An0n

0

ต่อไปนี้เป็นสคริปต์เพื่อทำให้โซลูชันการวางท่อ grep เป็นอัตโนมัติ:

#!/bin/bash

# Use filename if provided as environment variable, or "foo" as default
filename=${filename-foo}

grepand () {
# disable word splitting and globbing
IFS=
set -f
if [[ -n $1 ]]
then
grep -i "$1" ${filename} | filename="" grepand "${@:2}"
else
# If there are no arguments, assume last command in pipe and print everything
cat
fi
}

grepand "$@"

1
สิ่งนี้น่าจะถูกนำมาใช้โดยใช้ฟังก์ชั่นวนซ้ำแทนที่จะสร้างสตริงคำสั่งevalแล้วนำไปแบ่งซึ่งจะแตกง่าย
muru

@muru รู้สึกฟรีเพื่อแนะนำการแก้ไข ฉันขอขอบคุณความคิดเห็น
เจฟฟ์

1
การแก้ไขมันจะเป็นการเขียนมากเกินไปดังนั้นฉันจะไม่ทำเช่นนั้น ถ้าคุณต้องการที่จะเพิ่มนี่คือสิ่งที่ฉันคิดว่ามันควรมีลักษณะ: paste.ubuntu.com/23915379
muru
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.