Cocokkan dua string dalam satu baris dengan grep

218

Saya mencoba menggunakan grepuntuk mencocokkan garis yang berisi dua string berbeda. Saya telah mencoba yang berikut tetapi ini cocok dengan baris yang berisi string1 atau string2 yang bukan yang saya inginkan.

grep 'string1\|string2' filename

Jadi bagaimana cara mencocokkan grephanya dengan garis yang berisi kedua string ?

hearsaxas
sumber
1
Terkait: unix.stackexchange.com/questions/37313/…
AlikElzin-kilaka

Jawaban:

189

Kamu bisa memakai grep 'string1' filename | grep 'string2'

Atau, grep 'string1.*string2\|string2.*string1' filename

dheerosaur
sumber
5
@AlexanderN memang saya tidak bisa membuatnya bekerja dengan multiline, itu sangat aneh diterima ..
Aquarius Power
1
Itu bukan pertanyaan multiline. Jika multiline, grep -P mendukung reg style Perl ...
Scott Prive
20
Hanya berfungsi ketika kedua 'string1' DAN 'string2' berada di baris yang sama. Jika Anda ingin menemukan baris dengan 'string1' atau 'string2', lihat jawaban user45949.
lifeson106
10
opsi pertama: memipis satu grep ke detik TIDAK menghasilkan ATAU hasil yang menghasilkan DAN.
masukomi
1
Saya menggunakangrep -e "string1" -e "string2"
Ravi Dhoriya ツ
198

Saya pikir ini yang Anda cari:

grep -E "string1|string2" filename

Saya pikir jawabannya seperti ini:

grep 'string1.*string2\|string2.*string1' filename

hanya cocok dengan kasus di mana keduanya ada, bukan satu atau yang lain atau keduanya.

pengguna45949
sumber
14
tidak akan grep -e "string1" -e "string2" filenamemelakukan hal yang sama?
janosdivenyi
25
ini adalah bagaimana grep untuk string1 ATAU string2. pertanyaannya dengan jelas menyatakan mereka sedang mencari string1 AND string2.
orion elenzil
9
Cukup yakin bahwa pertanyaannya cukup tepat:How do I match lines that contains *both* strings?
r0estir0bbe
Bisakah itu mencetak dengan garis yang sama?
吴毅 凡
1
Mengapa jawaban ini masih ada di sini? Ini BUKAN jawaban untuk pertanyaan itu.
Prometheus
26

Untuk mencari file yang mengandung semua kata dalam urutan apa pun di mana saja:

grep -ril \'action\' | xargs grep -il \'model\' | xargs grep -il \'view_type\'

Grep pertama memulai pencarian rekursif ( r), mengabaikan case ( i) dan daftar (mencetak) nama file yang cocok ( l) untuk satu istilah ( 'action'dengan tanda kutip tunggal) yang terjadi di mana saja dalam file.

Greps selanjutnya mencari istilah-istilah lain, mempertahankan ketidakpekaan huruf besar dan mencantumkan file-file yang cocok.

Daftar akhir file yang akan Anda dapatkan adalah file-file yang berisi istilah-istilah ini, dalam urutan apa pun di dalam file tersebut.

Kinjal Dixit
sumber
2
Sepakat! Saya hanya akan mencatat bahwa saya harus memberi xarg "-d '\ n'" untuk menangani nama file dengan spasi. Ini bekerja untuk saya di Linux: grep -ril 'foo' | xargs -d '\n' grep -il 'bar'
Tommy Harris
16

Jika Anda memiliki grepdengan -Ppilihan untuk terbatas perlregex, Anda dapat menggunakan

grep -P '(?=.*string1)(?=.*string2)'

yang memiliki keuntungan bekerja dengan string yang tumpang tindih. Ini agak lebih mudah menggunakan perlsebagai grep, karena Anda dapat menentukan dan logika lebih langsung:

perl -ne 'print if /string1/ && /string2/'
tchrist
sumber
1
Jawaban Terbaik. Shell sangat mudah dan cepat, tetapi begitu polanya menjadi kompleks, Anda harus menggunakan Python atau Perl (atau Awk). Jangan membenturkan kepala Anda ke dinding mencoba membuktikannya dapat dilakukan dalam cangkang murni (apa pun artinya hari ini). Orang-orang pengingat, alat-alat ini dapat digunakan dalam sintaks "satu liner" yang ditanamkan dibble ke dalam skrip shell yang ada.
Scott Prive
12

Metode Anda hampir bagus, hanya melewatkan -w

grep -w 'string1\|string2' filename
Leo
sumber
1
Setidaknya pada OS-X dan FreeBSD tidak berfungsi! Dugaan saya adalah Anda menggunakan sesuatu yang lain (yang tidak didefinisikan OP - harap Anda tidak menurunkan jawaban yang benar untuk banyak pengguna kecuali Anda).
Leo
Saya menggunakan OS-X. Mungkin saya tidak melakukan ini dengan benar? Lihatlah apa yang saya lakukan: i.imgur.com/PFVlVAG.png
Ariel
1
Aneh. Saya berharap perbedaannya adalah tidak masuk ke file, tetapi, jika saya menyalurkan metode saya dengan ls Anda, saya mendapatkan hasil yang tidak Anda dapatkan: imgur.com/8eTt3Ak.png - Keduanya di kedua OS-X 10.9.5 ( "grep (BSD grep) 2.5.1-FreeBSD") dan FreeBSD 10 ("grep (GNU grep) 2.5.1-FreeBSD"). Saya ingin tahu apa milikmu grep -V.
Leo
1
Contoh Anda berfungsi untuk saya: i.imgur.com/K8LM69O.png Jadi perbedaannya adalah bahwa metode ini tidak mengambil substring, mereka harus menyelesaikan string sendiri. Saya kira Anda perlu membangun regexps di grep untuk mencari substring. Sesuatu seperti ini:grep -w 'regexp1\|regexp2' filename
Ariel
2
OP menunjukkan contoh dengan mencocokkan string1 atau string2 dan bertanya bagaimana cara mencocokkan garis yang berisi kedua string. Contoh ini masih menghasilkan ATAU.
gustafbstrom
7

The |operator dalam ekspresi reguler berarti atau. Artinya string1 atau string2 akan cocok. Anda bisa melakukannya:

grep 'string1' filename | grep 'string2'

yang akan menyalurkan hasil dari perintah pertama ke grep kedua. Itu seharusnya memberi Anda hanya garis yang cocok dengan keduanya.

martineno
sumber
1
Pernyataan Anda benar, tetapi jangan menjawab pertanyaan OP
Ben Wheeler
Ini memang menjawab pertanyaan dan memang begitulah cara kebanyakan orang menulisnya.
Peter K
7

Anda dapat mencoba sesuatu seperti ini:

(pattern1.*pattern2|pattern2.*pattern1)
Dorn
sumber
4

Dan seperti yang disarankan orang perl dan python, dan skrip shell yang berbelit-belit, berikut pendekatan awk yang sederhana :

awk '/string1/ && /string2/' filename

Setelah melihat komentar pada jawaban yang diterima: tidak, ini tidak multi-line; tapi kemudian bukan itu yang diminta oleh penulis pertanyaan itu.

Tink
sumber
3

Jangan mencoba menggunakan grep untuk ini, gunakan awk sebagai gantinya. Untuk mencocokkan 2 regexps R1 dan R2 di grep, Anda akan mengira:

grep 'R1.*R2|R2.*R1'

sementara di awk itu akan menjadi:

awk '/R1/ && /R2/'

tetapi bagaimana jika R2tumpang tindih dengan atau merupakan subset dari R1? Perintah grep itu tidak akan berfungsi sementara perintah awk akan berhasil. Katakanlah Anda ingin menemukan garis yang berisi thedan heat:

$ echo 'theatre' | grep 'the.*heat|heat.*the'
$ echo 'theatre' | awk '/the/ && /heat/'
theatre

Anda harus menggunakan 2 greps dan pipa untuk itu:

$ echo 'theatre' | grep 'the' | grep 'heat'
theatre

dan tentu saja jika Anda benar-benar meminta mereka untuk terpisah Anda selalu dapat menulis dalam awk regexp yang sama seperti yang Anda gunakan dalam grep dan ada solusi awk alternatif yang tidak melibatkan pengulangan regexps dalam setiap urutan yang mungkin.

Kesampingkan itu, bagaimana jika Anda ingin memperluas solusi Anda untuk mencocokkan 3 regexps R1, R2, dan R3. Dalam grep itu akan menjadi salah satu pilihan yang buruk ini:

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

sementara di awk itu akan menjadi singkat, jelas, sederhana, efisien:

awk '/R1/ && /R2/ && /R3/'

Sekarang, bagaimana jika Anda benar-benar ingin mencocokkan string literal S1 dan S2, bukan regexps R1 dan R2? Anda tidak bisa melakukan itu dalam satu panggilan ke grep, Anda harus menulis kode untuk menghindari semua meteor RE sebelum memanggil grep:

S1=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R1')
S2=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R2')
grep 'S1.*S2|S2.*S1'

atau lagi gunakan 2 greps dan pipa:

grep -F 'S1' file | grep -F 'S2'

yang lagi-lagi adalah pilihan yang buruk sedangkan dengan awk Anda cukup menggunakan operator string daripada operator regexp:

awk 'index($0,S1) && index($0.S2)'

Sekarang, bagaimana jika Anda ingin mencocokkan 2 regexps dalam sebuah paragraf daripada sebuah baris? Tidak bisa dilakukan di grep, sepele di awk:

awk -v RS='' '/R1/ && /R2/'

Bagaimana dengan seluruh file? Sekali lagi tidak dapat dilakukan dalam grep dan sepele dalam awk (kali ini saya menggunakan awk GNU untuk multi-char RS untuk keringkasan tetapi tidak lebih banyak kode dalam awk atau Anda dapat memilih kontrol-char yang Anda tahu tidak akan menjadi input bagi RS untuk melakukan hal yang sama):

awk -v RS='^$' '/R1/ && /R2/'

Jadi - jika Anda ingin menemukan beberapa regexps atau string dalam satu baris atau paragraf atau file maka jangan gunakan grep, gunakan awk.

Ed Morton
sumber
Apakah awk '/R1/ && /R2/'case-insensitive?
Prometheus
@Hashim - tidak. Untuk membuatnya case-sensitive dengan GNU awk yang akan Anda lakukan awk -v IGNORECASE=1 '/R1/ && /R2/'dan dengan awkawk '{x=toupper($0)} x~/R1/ && x~/R2/'
Ed Morton
3
grep string1\|string2 FILENAME 

GNU grep versi 3.1

tilikoom
sumber
2

Ditemukan garis yang hanya dimulai dengan 6 spasi dan selesai dengan:

 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
Cristian
sumber
2

Katakanlah kita perlu mencari hitungan beberapa kata dalam file testfile. Ada dua cara untuk melakukannya

1) Gunakan perintah grep dengan pola pencocokan regex

grep -c '\<\(DOG\|CAT\)\>' testfile

2) Gunakan perintah egrep

egrep -c 'DOG|CAT' testfile 

Dengan egrep Anda tidak perlu khawatir tentang ekspresi dan hanya memisahkan kata-kata dengan pemisah pipa.

Amit Singh
sumber
2

git grep

Berikut ini sintaks yang digunakan git grepdengan beberapa pola:

git grep --all-match --no-index -l -e string1 -e string2 -e string3 file

Anda juga dapat menggabungkan pola dengan ekspresi Boolean seperti --and, --ordan --not.

Periksa man git-grepbantuan.


--all-matchSaat memberikan beberapa ekspresi pola, tanda ini ditentukan untuk membatasi kecocokan pada file yang memiliki garis yang cocok dengan semuanya .

--no-index Cari file di direktori saat ini yang tidak dikelola oleh Git.

-l/ --files-with-matches/ --name-onlyTampilkan hanya nama file.

-eParameter selanjutnya adalah polanya. Default adalah menggunakan regexp dasar.

Params lain yang perlu dipertimbangkan:

--threads Jumlah utas pekerja grep yang akan digunakan.

-q/ --quiet/ --silentJangan output garis yang cocok; keluar dengan status 0 saat ada kecocokan.

Untuk mengubah jenis pola, Anda juga dapat menggunakan -G/ --basic-regexp(default), -F/ --fixed-strings, -E/ --extended-regexp, -P/ --perl-regexp, -f file, dan lainnya.

Terkait:

Untuk operasi ATAU , lihat:

kenorb
sumber
2
Selalu berpikir bahwa "git grep" hanya dapat dijalankan di dalam repositori git. Saya Tidak mengetahui opsi --no-index. Terima kasih telah menunjukkannya!
Kamaraju Kusumanchi
1

Tempatkan string yang ingin Anda ambil dalam file

echo who    > find.txt
echo Roger >> find.txt
echo [44][0-9]{9,} >> find.txt

Kemudian cari menggunakan -f

grep -f find.txt BIG_FILE_TO_SEARCH.txt 
Benih Tim
sumber
1
grep '(string1.*string2 | string2.*string1)' filename

akan mendapatkan baris dengan string1 dan string2 dalam urutan apa pun

James
sumber
Dengan cara apa hal itu berbeda dari setidaknya dua jawaban teratas?
luk2302
1
grep -i -w 'string1\|string2' filename

Ini berfungsi untuk pencocokan kata yang tepat dan kata yang tidak sensitif untuk huruf yang cocok, untuk itu -i digunakan

Saurabh
sumber
0

untuk pertandingan multiline:

echo -e "test1\ntest2\ntest3" |tr -d '\n' |grep "test1.*test3"

atau

echo -e "test1\ntest5\ntest3" >tst.txt
cat tst.txt |tr -d '\n' |grep "test1.*test3\|test3.*test1"

kita hanya perlu menghapus karakter baris baru dan itu berfungsi!

Aquarius Power
sumber
0

Anda seharusnya grepseperti ini:

$ grep 'string1' file | grep 'string2'
Raghuram
sumber
1
Ini melakukan DAN logis. OP ingin OR logis.
Ben Wheeler
1
@ BenWheeler: Dari pertanyaan: "Jadi, bagaimana saya mencocokkan dengan grep hanya garis yang mengandung kedua string?"
Erik I
0

Saya sering mengalami masalah yang sama seperti masalah Anda, dan saya baru saja menulis sebuah skrip:

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
}

Pemakaian:

echo "a b c" | m -p A 
echo "a b c" | m -i -p A # a b c

Anda bisa memasukkannya ke dalam .bashrc jika mau.

ruanhao
sumber
0

Ketika kedua string berada di urutan kemudian menempatkan pola di antara grepperintah:

$ grep -E "string1(?.*)string2" file

Contoh jika baris berikut ini terkandung dalam file bernama Dockerfile:

FROM python:3.8 as build-python
FROM python:3.8-slim

Untuk mendapatkan garis yang berisi string: FROM pythondan as build-pythonkemudian gunakan:

$ grep -E "FROM python:(?.*) as build-python" Dockerfile

Maka output hanya akan menampilkan baris yang berisi kedua string :

FROM python:3.8 as build-python
Chetabahana
sumber
-2

ripgrep

Berikut ini contoh penggunaannya rg:

rg -N '(?P<p1>.*string1.*)(?P<p2>.*string2.*)' file.txt

Ini adalah salah satu alat grepping tercepat, karena itu dibangun di atas mesin regex Rust yang menggunakan automata terbatas, SIMD dan optimisasi literal agresif untuk membuat pencarian sangat cepat.

Gunakan itu, terutama ketika Anda sedang bekerja dengan data besar.

Lihat juga permintaan fitur terkait di GH-875 .

kenorb
sumber
1
Jawaban ini kurang tepat. Grup penangkap yang dinamai tidak perlu, dan ini tidak menangani kasing ketika string2muncul sebelumnya string1. Solusi paling sederhana untuk masalah ini adalah rg string1 file.txt | rg string2.
BurntSushi5