ฉันพยายามใช้grep
เพื่อจับคู่บรรทัดที่มีสองสตริงที่แตกต่างกัน ฉันลองต่อไปนี้ แต่ตรงกับบรรทัดที่มีทั้งstring1 หรือ string2ซึ่งไม่ใช่สิ่งที่ฉันต้องการ
grep 'string1\|string2' filename
ดังนั้นฉันจะจับคู่กับgrep
บรรทัดที่มีทั้งสองสตริงได้อย่างไร
ฉันพยายามใช้grep
เพื่อจับคู่บรรทัดที่มีสองสตริงที่แตกต่างกัน ฉันลองต่อไปนี้ แต่ตรงกับบรรทัดที่มีทั้งstring1 หรือ string2ซึ่งไม่ใช่สิ่งที่ฉันต้องการ
grep 'string1\|string2' filename
ดังนั้นฉันจะจับคู่กับgrep
บรรทัดที่มีทั้งสองสตริงได้อย่างไร
คำตอบ:
คุณสามารถใช้ได้ grep 'string1' filename | grep 'string2'
หรือ, grep 'string1.*string2\|string2.*string1' filename
grep -e "string1" -e "string2"
ฉันคิดว่านี่คือสิ่งที่คุณกำลังมองหา:
grep -E "string1|string2" filename
ฉันคิดว่าคำตอบเช่นนี้:
grep 'string1.*string2\|string2.*string1' filename
ตรงกับกรณีที่ทั้งสองมีอยู่ไม่หนึ่งหรืออื่น ๆ หรือทั้งสองอย่าง
grep -e "string1" -e "string2" filename
ทำเช่นเดียวกัน?
How do I match lines that contains *both* strings?
หากต้องการค้นหาไฟล์ที่มีคำทั้งหมดในลำดับใดก็ได้:
grep -ril \'action\' | xargs grep -il \'model\' | xargs grep -il \'view_type\'
grep แรกเริ่มต้นจากการค้นหาแบบเรียกซ้ำ ( r
), ไม่สนใจขนาดตัวพิมพ์ ( i
) และรายชื่อ (พิมพ์ออกมา) ชื่อของไฟล์ที่กำลังจับคู่ ( l
) สำหรับหนึ่งคำ ('action'
ที่มีราคาเดียว) ที่เกิดขึ้นได้ทุกที่ในไฟล์
greps ที่ตามมาค้นหาคำอื่น ๆ รักษาความไม่รู้สึกตัวเล็กและรายการออกไฟล์ที่ตรงกัน
รายการสุดท้ายของไฟล์ที่คุณจะได้รับคือไฟล์ที่มีข้อกำหนดเหล่านี้ในลำดับใดก็ได้ในไฟล์
grep -ril 'foo' | xargs -d '\n' grep -il 'bar'
หากคุณมีgrep
กับ-P
ตัวเลือกสำหรับการ จำกัดperl
regex คุณสามารถใช้
grep -P '(?=.*string1)(?=.*string2)'
ซึ่งมีข้อได้เปรียบในการทำงานกับสตริงที่ทับซ้อนกัน มันค่อนข้างตรงไปตรงมาใช้perl
เป็นgrep
เพราะคุณสามารถระบุและตรรกะได้โดยตรง:
perl -ne 'print if /string1/ && /string2/'
วิธีการของคุณเกือบจะดีหาย -w
grep -w 'string1\|string2' filename
grep -V
เป็นอะไร
grep -w 'regexp1\|regexp2' filename
|
ผู้ประกอบการในการแสดงออกปกติหรือหมายความว่า กล่าวคือ string1 หรือ string2 จะตรงกัน คุณสามารถทำได้:
grep 'string1' filename | grep 'string2'
ซึ่งจะไพพ์ผลลัพธ์จากคำสั่งแรกไปยัง grep ตัวที่สอง ที่ควรให้เฉพาะบรรทัดที่ตรงกับทั้งคู่
คุณสามารถลองสิ่งนี้:
(pattern1.*pattern2|pattern2.*pattern1)
และเป็นคนที่แนะนำ Perl และงูหลามและซับซ้อนเชลล์สคริปต์ที่นี่ง่ายawkวิธีการ:
awk '/string1/ && /string2/' filename
ต้องดูความคิดเห็นต่อคำตอบที่ยอมรับได้: ไม่นี่ไม่ได้ทำหลายบรรทัด แต่นั่นไม่ใช่สิ่งที่ผู้เขียนคำถามถาม
อย่าพยายามใช้ grep สำหรับเรื่องนี้ใช้ awk แทน ในการจับคู่ 2 regexps R1 และ R2 ใน grep คุณคิดว่ามันจะเป็น:
grep 'R1.*R2|R2.*R1'
ในขณะที่ awk มันจะเป็น:
awk '/R1/ && /R2/'
แต่ถ้าR2
การทับซ้อนที่มีหรือเป็นส่วนหนึ่งของR1
? คำสั่ง grep นั้นจะไม่ทำงานในขณะที่คำสั่ง awk จะทำงาน ให้บอกว่าคุณต้องการค้นหาบรรทัดที่มีthe
และheat
:
$ echo 'theatre' | grep 'the.*heat|heat.*the'
$ echo 'theatre' | awk '/the/ && /heat/'
theatre
คุณต้องใช้ 2 greps และไปป์:
$ echo 'theatre' | grep 'the' | grep 'heat'
theatre
และแน่นอนว่าถ้าคุณต้องการให้แยกมันออกมาคุณสามารถเขียน regexp แบบเดียวกับที่คุณใช้ใน grep และมีตัวเลือก awk ทางเลือกที่ไม่เกี่ยวข้องกับการทำซ้ำ regexps ในทุกลำดับที่เป็นไปได้
หากคุณต้องการขยายโซลูชันของคุณให้ตรงกับ 3 regexps R1, R2 และ R3 ใน grep ที่เป็นหนึ่งในตัวเลือกที่ไม่ดีเหล่านี้:
grep 'R1.*R2.*R3|R1.*R3.*R2|R2.*R1.*R3|R2.*R3.*R1|R3.*R1.*R2|R3.*R2.*R1' file
grep R1 file | grep R2 | grep R3
ในขณะที่อยู่ใน awk มันจะกระชับชัดเจนง่ายมีประสิทธิภาพ:
awk '/R1/ && /R2/ && /R3/'
ทีนี้ถ้าคุณต้องการจับคู่สตริง S1 และ S2 แทน regexps R1 และ R2 คุณไม่สามารถทำเช่นนั้นได้ในการเรียก grep เพียงครั้งเดียวคุณต้องเขียนโค้ดเพื่อหลบหลีกเมตาคาร์ RE ทั้งหมดก่อนที่จะเรียก grep:
S1=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R1')
S2=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R2')
grep 'S1.*S2|S2.*S1'
หรือใช้ 2 greps และไปป์อีกครั้ง:
grep -F 'S1' file | grep -F 'S2'
ซึ่งเป็นตัวเลือกที่ไม่ดีอีกครั้งในขณะที่ awk คุณเพียงแค่ใช้ตัวดำเนินการสตริงแทนตัวดำเนินการ regexp:
awk 'index($0,S1) && index($0.S2)'
ทีนี้ถ้าคุณต้องการจับคู่ 2 regexps ในย่อหน้ามากกว่าหนึ่งบรรทัด ไม่สามารถทำได้ใน grep, เล็กน้อยใน awk:
awk -v RS='' '/R1/ && /R2/'
แล้วไฟล์ทั้งหมดล่ะ? ไม่สามารถทำได้ใน grep และ trivial ใน awk อีกครั้ง (ตอนนี้ฉันใช้ GNU awk สำหรับ multi-char RS สำหรับความรัดกุม แต่มันไม่ได้รหัสมากขึ้นใน awk ใด ๆ หรือคุณสามารถเลือกตัวควบคุม-char ที่คุณรู้ว่าจะไม่ อยู่ในอินพุตสำหรับ RS เพื่อทำสิ่งเดียวกัน):
awk -v RS='^$' '/R1/ && /R2/'
ดังนั้น - หากคุณต้องการค้นหาหลาย ๆ regexps หรือสายอักขระในบรรทัดหรือย่อหน้าหรือไฟล์แล้วไม่ใช้ grep ใช้ awk
awk '/R1/ && /R2/'
ตามตัวพิมพ์ใหญ่ - เล็ก
awk -v IGNORECASE=1 '/R1/ && /R2/'
และกับ awk ใด ๆawk '{x=toupper($0)} x~/R1/ && x~/R2/'
grep ‘string1\|string2’ FILENAME
GNU grep เวอร์ชั่น 3.1
พบบรรทัดที่เริ่มต้นด้วย 6 ช่องว่างและจบด้วย:
cat my_file.txt | grep
-e '^ .*(\.c$|\.cpp$|\.h$|\.log$|\.out$)' # .c or .cpp or .h or .log or .out
-e '^ .*[0-9]\{5,9\}$' # numers between 5 and 9 digist
> nolog.txt
สมมติว่าเราต้องการค้นหาคำหลายคำในไฟล์ testfile มีสองวิธีที่จะไปเกี่ยวกับเรื่องนี้
1) ใช้คำสั่ง grep กับรูปแบบการจับคู่ regex
grep -c '\<\(DOG\|CAT\)\>' testfile
2) ใช้คำสั่ง egrep
egrep -c 'DOG|CAT' testfile
ด้วย egrep คุณไม่จำเป็นต้องกังวลเกี่ยวกับการแสดงออกและเพียงแค่แยกคำด้วยตัวคั่นท่อ
git grep
นี่คือไวยากรณ์ที่ใช้git grep
กับหลายรูปแบบ:
git grep --all-match --no-index -l -e string1 -e string2 -e string3 file
นอกจากนี้คุณยังอาจรวมรูปแบบกับบูลีนการแสดงออกเช่น--and
, และ--or
--not
ตรวจสอบman git-grep
ความช่วยเหลือ
--all-match
เมื่อให้การแสดงออกรูปแบบหลายสถานะนี้มีการระบุการจำกัด การแข่งขันไปยังไฟล์ที่มีสายเพื่อให้ตรงกับทั้งหมดของพวกเขา
--no-index
ค้นหาไฟล์ในไดเรกทอรีปัจจุบันที่ไม่ได้รับการจัดการโดย Git
-l
/--files-with-matches
/--name-only
แสดงเฉพาะชื่อไฟล์
-e
พารามิเตอร์ถัดไปคือรูปแบบ เริ่มต้นคือการใช้ regexp ขั้นพื้นฐาน
พารามิเตอร์อื่น ๆ ที่ควรพิจารณา:
--threads
จำนวนเธรดผู้ทำงาน grep ที่จะใช้
-q
/--quiet
/--silent
อย่าส่งออกเส้นที่ตรงกัน; ออกด้วยสถานะ 0 เมื่อมีการแข่งขัน
หากต้องการเปลี่ยนประเภทรูปแบบคุณอาจใช้-G
/ --basic-regexp
(ค่าเริ่มต้น), -F
/ --fixed-strings
, -E
/ --extended-regexp
, -P
/ --perl-regexp
,-f file
และอื่น ๆ
ที่เกี่ยวข้อง:
สำหรับหรือการดำเนินการโปรดดูที่:
วางสตริงที่คุณต้องการ grep ไว้ในไฟล์
echo who > find.txt
echo Roger >> find.txt
echo [44][0-9]{9,} >> find.txt
จากนั้นค้นหาโดยใช้ -f
grep -f find.txt BIG_FILE_TO_SEARCH.txt
grep '(string1.*string2 | string2.*string1)' filename
จะได้รับสายกับ string1 และ string2 ในลำดับใด ๆ
grep -i -w 'string1\|string2' filename
สิ่งนี้ใช้ได้กับการจับคู่คำที่ตรงกันและคำที่ไม่ตรงตามตัวพิมพ์ใหญ่ - เล็กสำหรับการใช้ -i
สำหรับการแข่งขันหลายสาย:
echo -e "test1\ntest2\ntest3" |tr -d '\n' |grep "test1.*test3"
หรือ
echo -e "test1\ntest5\ntest3" >tst.txt
cat tst.txt |tr -d '\n' |grep "test1.*test3\|test3.*test1"
เราเพียงแค่ต้องการลบอักขระบรรทัดใหม่และใช้งานได้!
คุณควรมีgrep
สิ่งนี้:
$ grep 'string1' file | grep 'string2'
ฉันมักพบปัญหาเดียวกับของคุณและฉันเพิ่งเขียนสคริปต์บางส่วน:
function m() { # m means 'multi pattern grep'
function _usage() {
echo "usage: COMMAND [-inH] -p<pattern1> -p<pattern2> <filename>"
echo "-i : ignore case"
echo "-n : show line number"
echo "-H : show filename"
echo "-h : show header"
echo "-p : specify pattern"
}
declare -a patterns
# it is important to declare OPTIND as local
local ignorecase_flag filename linum header_flag colon result OPTIND
while getopts "iHhnp:" opt; do
case $opt in
i)
ignorecase_flag=true ;;
H)
filename="FILENAME," ;;
n)
linum="NR," ;;
p)
patterns+=( "$OPTARG" ) ;;
h)
header_flag=true ;;
\?)
_usage
return ;;
esac
done
if [[ -n $filename || -n $linum ]]; then
colon="\":\","
fi
shift $(( $OPTIND - 1 ))
if [[ $ignorecase_flag == true ]]; then
for s in "${patterns[@]}"; do
result+=" && s~/${s,,}/"
done
result=${result# && }
result="{s=tolower(\$0)} $result"
else
for s in "${patterns[@]}"; do
result="$result && /$s/"
done
result=${result# && }
fi
result+=" { print "$filename$linum$colon"\$0 }"
if [[ ! -t 0 ]]; then # pipe case
cat - | awk "${result}"
else
for f in "$@"; do
[[ $header_flag == true ]] && echo "########## $f ##########"
awk "${result}" $f
done
fi
}
การใช้งาน:
echo "a b c" | m -p A
echo "a b c" | m -i -p A # a b c
คุณสามารถใส่ไว้ใน. bashrc หากคุณต้องการ
เมื่อทั้งสองสายอยู่ในลำดับแล้ววางรูปแบบในระหว่างgrep
คำสั่งเมื่อ:
$ grep -E "string1(?.*)string2" file
ตัวอย่างถ้าบรรทัดต่อไปนี้มีอยู่ในไฟล์ชื่อDockerfile
:
FROM python:3.8 as build-python
FROM python:3.8-slim
ที่จะได้รับบรรทัดที่ประกอบด้วยสตริง: FROM python
และas build-python
การใช้งานแล้ว:
$ grep -E "FROM python:(?.*) as build-python" Dockerfile
จากนั้นเอาต์พุตจะแสดงเฉพาะบรรทัดที่มีทั้งสองสตริง :
FROM python:3.8 as build-python
ripgrep
นี่คือตัวอย่างการใช้rg
:
rg -N '(?P<p1>.*string1.*)(?P<p2>.*string2.*)' file.txt
เป็นหนึ่งในเครื่องมือ grepping ที่เร็วที่สุดเนื่องจากมันถูกสร้างขึ้นจากเครื่องมือ regex ของ Rustซึ่งใช้ออโต้ไฟน์ จำกัด , SIMD และการเพิ่มประสิทธิภาพตัวอักษรที่ก้าวร้าวเพื่อให้การค้นหารวดเร็วมาก
ใช้มันโดยเฉพาะเมื่อคุณทำงานกับข้อมูลขนาดใหญ่
string2
ปรากฏก่อนหน้าstring1
นี้ rg string1 file.txt | rg string2
ทางออกที่ง่ายที่สุดในการแก้ไขปัญหานี้คือ