Bagaimana cara grep untuk beberapa pola dengan pola yang memiliki karakter pipa?


623

Saya ingin menemukan semua baris dalam beberapa file yang cocok dengan salah satu dari dua pola. Saya mencoba menemukan pola yang saya cari dengan mengetik

grep (foo|bar) *.txt

tetapi shell mengartikannya |sebagai pipa dan mengeluh kapanbar tidak dapat dieksekusi.

Bagaimana saya bisa mendapatkan beberapa pola dalam kumpulan file yang sama?



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

Jawaban:


860

Pertama, Anda perlu melindungi pola dari ekspansi oleh shell. Cara termudah untuk melakukannya adalah dengan menempatkan tanda kutip tunggal di sekitarnya. Kutipan tunggal mencegah perluasan apa pun di antara mereka (termasuk backslash); satu-satunya hal yang tidak dapat Anda lakukan adalah memiliki tanda kutip tunggal dalam pola.

grep 'foo*' *.txt

Jika Anda memang membutuhkan satu kutipan, Anda dapat menuliskannya sebagai '\''(string akhir literal, kutipan literal, string terbuka literal).

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

Kedua, grep mendukung dua sintaks untuk pola. Sintaks default yang lama ( ekspresi reguler dasar ) tidak mendukung |operator alternation ( ), meskipun beberapa versi memilikinya sebagai ekstensi, tetapi ditulis dengan backslash.

grep 'foo\|bar' *.txt

Cara portabel adalah dengan menggunakan sintaks yang lebih baru, ekspresi reguler yang diperluas . Anda harus melewati -Eopsi grepuntuk memilihnya. Di Linux, Anda juga bisa mengetik egrepalih-alih grep -E(di unices lain, Anda bisa menjadikannya sebagai alias).

grep -E 'foo|bar' *.txt

Kemungkinan lain ketika Anda hanya mencari salah satu dari beberapa pola (sebagai lawan membangun pola kompleks menggunakan disjungsi) adalah meneruskan beberapa pola grep. Anda dapat melakukan ini dengan mendahului setiap pola dengan -eopsi.

grep -e foo -e bar *.txt

18
Sebagai sidenote - ketika polanya diperbaiki, Anda harus benar-benar membiasakan diri fgrepatau grep -F, untuk pola kecil perbedaannya dapat diabaikan tetapi seiring bertambahnya waktu, manfaatnya mulai terlihat ...
TC1

7
@ TC1 fgrep tidak digunakan lagi menurut halaman manual
ramn

18
@ TC1 Apakah grep -Fmemiliki manfaat kinerja aktual tergantung pada implementasi grep: beberapa dari mereka menerapkan algoritma yang sama, sehingga -Fmembuat perbedaan hanya pada waktu yang dihabiskan untuk menguraikan pola dan tidak untuk pencarian waktu. GNU grep tidak lebih cepat dengan -F, misalnya (ia juga memiliki bug yang membuat grep -Flebih lambat di lokal multibyte - pola konstan yang sama dengan grepsebenarnya jauh lebih cepat!). Di sisi lain BusyBox grep memang mendapat banyak manfaat dari -Fpada file besar.
Gilles

4
Mungkin perlu disebutkan bahwa untuk pola yang lebih rumit di mana pergantian hanya untuk bagian dari ekspresi reguler, itu dapat dikelompokkan dengan "\ (" dan "\)" (yang melarikan diri adalah untuk default "ekspresi reguler dasar" ) (?).
Peter Mortensen

4
Perhatikan bahwa ada egrepsebelum grep -E. Ini bukan spesifik GNU (tentu tidak ada hubungannya dengan Linux). Sebenarnya, Anda masih akan menemukan sistem seperti Solaris di mana standarnya grepmasih tidak mendukung -E.
Stéphane Chazelas

90
egrep "foo|bar" *.txt

atau

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

secara selektif mengutip halaman manual 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.

Pada awalnya saya tidak membaca lebih lanjut, jadi saya tidak mengenali perbedaan halus:

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

Saya selalu menggunakan egrep dan orangtua yang tidak perlu, karena saya belajar dari contoh. Sekarang saya belajar sesuatu yang baru. :)


22

Seperti kata TC1, -Fsepertinya ini adalah opsi yang bisa digunakan:

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

$> patterns="foo
bar" 

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

1
@poige Saya tidak tahu tentang opsi $ 'foo \ nbar', tidak yakin bagaimana ekspansi bekerja di sini, perlu mencari, tapi terima kasih, itu sangat berguna.
haridsv

Bagus! Opsi ini juga membuatnya berjalan lebih cepat (karena menonaktifkan regex).
qwertzguy

15

Pertama, Anda perlu menggunakan tanda kutip untuk karakter khusus. Kedua, meskipun demikian, greptidak akan mengerti pergantian secara langsung; Anda harus menggunakan egrep, atau ( grephanya dengan GNU ) grep -E.

egrep 'foo|bar' *.txt

(Kurung tidak perlu kecuali jika pergantian adalah bagian dari regex yang lebih besar.)


4
Sebenarnya, grep -Elebih standar daripada egrep.
jw013

8

Jika Anda tidak memerlukan ekspresi reguler, ini jauh lebih cepat untuk digunakan fgrepatau grep -Fdengan beberapa parameter e, seperti ini:

fgrep -efoo -ebar *.txt

fgrep(Atau grep -F) jauh lebih cepat daripada grep biasa karena ia mencari string tetap daripada ekspresi reguler.


4
Silakan lihat juga komentar di halaman ini yang menyebutkan bahwa fgrepitu sudah usang.
phk

6

Anda dapat mencoba perintah di bawah ini untuk mendapatkan hasilnya:

egrep 'rose.*lotus|lotus.*rose' some_file

3

Cara murah dan ceria untuk memahami berbagai pola:

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

Bisa mendapat manfaat dari penjelasan.
Peter Mortensen

2
Penjelasannya adalah bahwa -fopsi grep mengambil file dengan beberapa pola. Alih-alih membuat file sementara (yang mungkin Anda lupa hapus setelahnya), cukup gunakan subtitusi proses shell:grep -f <(echo foo; echo bar) *.txt
Jakob

3

Pipe ( |) adalah karakter shell khusus, jadi ia perlu diloloskan ( \|) atau dikutip sesuai manual ( man bash):

Mengutip digunakan untuk menghapus makna khusus karakter atau kata-kata tertentu ke shell. Ini dapat digunakan untuk menonaktifkan perlakuan khusus untuk karakter khusus, untuk mencegah kata-kata yang dicadangkan dari dikenali seperti itu, dan untuk mencegah ekspansi parameter.

Menutup karakter dalam tanda kutip ganda menjaga nilai literal semua karakter dalam tanda kutip

Garis miring terbalik ( \) adalah karakter pelarian.

Lihat: Karakter mana yang harus diloloskan di Bash?

Berikut adalah beberapa contoh (menggunakan alat yang belum disebutkan):

  • Menggunakan ripgrep:

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • Menggunakan git grep:

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

      Catatan: Ini juga mendukung ekspresi Boolean seperti --and, --ordan --not.

Untuk DAN operasi per baris, lihat: Bagaimana menjalankan grep dengan banyak pola DAN?

Untuk DAN operasi per file, lihat: Bagaimana memeriksa semua string atau regex yang ada di file?


3

Saya memiliki akses log di mana tanggal diformat dengan bodoh: [30 / Jun / 2013: 08: 00: 45 +0200]

Tapi saya perlu menampilkannya sebagai: 30 / Jun / 2013 08:00:45

Masalahnya adalah bahwa menggunakan "ATAU" dalam pernyataan grep saya, saya menerima dua ekspresi kecocokan pada dua baris terpisah.

Ini solusinya:

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: jika Anda ingin melakukan lebih banyak hal setelah mencocokkan salah satu dari beberapa pola, lampirkan seperti pada \(pattern1\|pattern2\)

contoh: Saya ingin menemukan semua tempat di mana variabel yang berisi nama 'tanggal' didefinisikan sebagai String atau int. (mis. "int cronDate =" atau "String textFormattedDateStamp ="):

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

Dengan grep -E, Anda tidak perlu melepaskan kurung atau pipa, yaitu,grep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='


1

Ini bekerja untuk saya

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

Ada beberapa cara untuk melakukan ini.

  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'

Opsi ke-3 dan ke-4 hanya akan menangkap file dan menghindari direktori memiliki .txtnama mereka.
Jadi, sesuai kasus penggunaan Anda, Anda dapat menggunakan salah satu opsi yang disebutkan di atas.
Terima kasih!!


0

untuk menambahkan jawaban @ geekosaur , jika Anda memiliki beberapa pola yang juga berisi tab dan ruang Anda menggunakan perintah berikut

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

di mana [[:blank:]]kelas karakter RE yang mewakili spasi atau karakter tab

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.