Saya ingin mencari file yang memiliki "abc" DAN "efg" dalam urutan itu, dan kedua string berada pada baris yang berbeda dalam file itu. Misalnya: file dengan konten:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
Harus dicocokkan.
Jawaban:
Grep tidak cukup untuk operasi ini.
pcregrep yang ditemukan di sebagian besar sistem Linux modern dapat digunakan sebagai
di mana
-M
,--multiline
izinkan pola untuk mencocokkan lebih dari satu barisAda pcre2grep yang lebih baru juga. Keduanya disediakan oleh proyek PCRE .
pcre2grep tersedia untuk Mac OS X melalui Mac Ports sebagai bagian dari port
pcre2
:dan melalui Homebrew sebagai:
atau untuk pcre2
pcre2grep juga tersedia di Linux (Ubuntu 18.04+)
sumber
-M, --multiline
- Mengizinkan pola cocok dengan lebih dari satu baris.'abc.*(\n|.)*?efg'
.*
->'abc(\n|.)*?efg'
untuk membuat regex lebih pendek (dan menjadi bertele-tele)pcregrep
memang membuat segalanya lebih mudah, tetapigrep
akan bekerja juga. Misalnya, lihat stackoverflow.com/a/7167115/123695Saya tidak yakin apakah itu mungkin dengan grep, tetapi sed membuatnya sangat mudah:
sumber
sed
, tetapi jika belum pernah melihat ekspresi seperti itu sebelumnya.Berikut adalah solusi yang terinspirasi oleh jawaban ini :
jika 'abc' dan 'efg' dapat berada di baris yang sama:
jika 'abc' dan 'efg' harus berada di baris yang berbeda:
Params:
-z
Perlakukan input sebagai satu set garis, masing-masing diakhiri dengan nol byte, bukan baris baru. yaitu grep memperlakukan input sebagai satu garis besar.-l
nama cetak dari setiap file input dari mana output biasanya akan dicetak.(?s)
aktifkan PCRE_DOTALL, yang berarti '.' menemukan karakter atau baris baru.sumber
l
. AFAIK tidak ada-1
opsi nomor .-z
opsi menentukan grep untuk memperlakukan baris baruzero byte characters
lalu mengapa kita perlu(?s)
di regex? Jika sudah menjadi karakter non-baris baru, bukankah.
harus dapat mencocokkannya secara langsung?sed harus cukup sebagai poster LJ yang disebutkan di atas,
alih-alih! d Anda cukup menggunakan p untuk mencetak:
sumber
Saya sangat mengandalkan pcregrep, tetapi dengan grep yang lebih baru Anda tidak perlu menginstal pcregrep untuk banyak fitur-fiturnya. Gunakan saja
grep -P
.Dalam contoh pertanyaan OP, saya pikir opsi berikut ini berfungsi dengan baik, dengan yang terbaik kedua mencocokkan dengan bagaimana saya memahami pertanyaan:
Saya menyalin teks sebagai / tmp / test1 dan menghapus 'g' dan disimpan sebagai / tmp / test2. Berikut adalah output yang menunjukkan bahwa yang pertama menunjukkan string yang cocok dan yang kedua hanya menunjukkan nama file (khas -o adalah untuk menunjukkan kecocokan dan -l khas adalah hanya menampilkan nama file). Perhatikan bahwa 'z' diperlukan untuk multiline dan '(. | \ N)' berarti untuk mencocokkan 'apa pun selain baris baru' atau 'baris baru' - yaitu apa pun:
Untuk menentukan apakah versi Anda cukup baru, jalankan
man grep
dan lihat apakah sesuatu yang mirip dengan ini muncul di dekat bagian atas:Itu dari GNU grep 2.10.
sumber
Ini dapat dilakukan dengan mudah dengan terlebih dahulu menggunakan
tr
untuk mengganti baris baru dengan beberapa karakter lain:Di sini, saya menggunakan karakter alarm,
\a
(ASCII 7) sebagai pengganti baris baru. Ini hampir tidak pernah ditemukan dalam teks Anda, dangrep
dapat mencocokkannya dengan.
, atau mencocokkannya dengan\a
.sumber
\0
dan karenanya diperlukangrep -a
dan cocok pada\x00
... Anda telah membantu saya menyederhanakan!echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'
sekarangecho $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
grep -o
.awk one-liner:
sumber
abc
hingga akhir file jika pola akhir tidak ada dalam file, atau pola akhir terakhir tidak ada. Anda dapat memperbaikinya tetapi itu akan menyulitkan skrip secara signifikan./efg/
dari output?Anda dapat melakukannya dengan sangat mudah jika Anda dapat menggunakan Perl.
Anda bisa melakukannya dengan ekspresi reguler tunggal juga, tetapi itu melibatkan mengambil seluruh isi file menjadi string tunggal, yang mungkin berakhir dengan mengambil terlalu banyak memori dengan file besar. Untuk kelengkapan, berikut adalah metode itu:
sumber
.*?
) untuk mendapatkan kecocokan minimal.Saya tidak tahu bagaimana saya akan melakukannya dengan grep, tetapi saya akan melakukan sesuatu seperti ini dengan awk:
Anda perlu berhati-hati dalam melakukannya. Apakah Anda ingin regex cocok dengan substring atau seluruh kata? tambahkan tag yang sesuai. Juga, sementara ini benar-benar sesuai dengan bagaimana Anda menyatakan contoh, itu tidak berfungsi ketika abc muncul kedua kalinya setelah efg. Jika Anda ingin mengatasinya, tambahkan if jika sesuai dalam / abc / case, dll.
sumber
Sayangnya, kamu tidak bisa. Dari
grep
dokumen:sumber
grep -Pz
Jika Anda ingin menggunakan konteks, ini bisa dicapai dengan mengetik
Ini akan menampilkan semuanya antara "abc" dan "efg", selama mereka berada dalam 500 baris satu sama lain.
sumber
Jika Anda membutuhkan kedua kata tersebut saling berdekatan, misalnya tidak lebih dari 3 baris, Anda dapat melakukan ini:
Contoh yang sama tetapi hanya memfilter file * .txt:
Dan juga Anda dapat mengganti
grep
perintah denganegrep
perintah jika Anda ingin juga menemukan dengan ekspresi reguler.sumber
Saya merilis alternatif grep beberapa hari yang lalu yang mendukung hal ini secara langsung, baik melalui pencocokan multiline atau menggunakan kondisi - semoga bermanfaat bagi sebagian orang yang mencari di sini. Beginilah perintah untuk contoh akan terlihat:
Multiline:
Kondisi:
Anda juga dapat menentukan bahwa 'efg' harus mengikuti 'abc' dalam sejumlah baris:
Anda dapat menemukan informasi lebih lanjut di sift-tool.org .
sumber
sift -lm 'abc.*efg' testfile
berhasil, karena pertandingan serakah dan melahap semua baris sampai yang terakhirefg
dalam file.Sementara opsi sed adalah yang paling sederhana dan termudah, one-liner LJ sayangnya bukan yang paling portabel. Mereka yang terjebak dengan versi C Shell harus melarikan diri dari poni mereka:
Sayangnya ini tidak berfungsi di bash et al.
sumber
sumber
Anda dapat menggunakan grep memetikan Anda tidak tertarik dalam urutan pola.
contoh
grep -l
akan menemukan semua file yang cocok dengan pola pertama, dan xargs akan memahami pola kedua. Semoga ini membantu.sumber
Dengan pencari perak :
mirip dengan jawaban pembawa cincin, tetapi dengan ag sebagai gantinya. Keuntungan kecepatan pencari perak mungkin bisa bersinar di sini.
sumber
(echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'
tidak cocokSaya menggunakan ini untuk mengekstrak urutan fasta dari file multi fasta menggunakan opsi -P untuk grep:
Inti dari regexp adalah
[^>]
yang diterjemahkan menjadi "tidak lebih besar dari simbol"sumber
Sebagai alternatif dari jawaban Balu Mohan, dimungkinkan untuk menegakkan urutan pola hanya menggunakan
grep
,head
dantail
:Yang ini tidak terlalu cantik. Diformat lebih mudah:
Ini akan mencetak nama semua file di mana
"pattern2"
muncul setelah"pattern1"
, atau di mana keduanya muncul pada baris yang sama :Penjelasan
tail -n +i
- cetak semua baris setelahi
, inklusifgrep -n
- baris yang cocok yang diawali dengan nomor baris merekahead -n1
- hanya mencetak baris pertamacut -d : -f 1
- cetak kolom potongan pertama menggunakan:
sebagai pembatas2>/dev/null
-tail
output kesalahan diam yang terjadi jika$()
ekspresi kembali kosonggrep -q
- diamgrep
dan kembali segera jika kecocokan ditemukan, karena kami hanya tertarik pada kode keluarsumber
&>
? Saya juga menggunakannya, tetapi saya tidak pernah melihatnya didokumentasikan di mana pun. BTW, mengapa kita harus membungkam grep seperti itu, sebenarnya?grep -q
tidak akan melakukan trik juga?&>
memberitahu bash untuk mengarahkan ulang output standar dan kesalahan standar, lihat REDIRECTION di manual bash. Anda sangat benar karena kita bisa melakukannya dengan baikgrep -q ...
daripadagrep ... &>/dev/null
, tangkapan yang bagus!Ini juga harus bekerja ?!
$ARGV
berisi nama file saat ini ketika membaca darifile_list /s
pencarian pengubah di baris baru.sumber
Filepattern
*.sh
penting untuk mencegah direktori diperiksa. Tentu saja beberapa tes bisa mencegahnya juga.Itu
mencari maksimum 1 yang cocok dan mengembalikan (-n) linenumber. Jika kecocokan ditemukan (tes -n ...) cari kecocokan terakhir dari efg (temukan semua dan ambil yang terakhir dengan ekor -n 1).
lain melanjutkan.
Karena hasilnya adalah sesuatu seperti
18:foofile.sh String alf="abc";
kita perlu memotong dari ":" hingga akhir baris.Harus mengembalikan hasil positif jika pertandingan terakhir dari ekspresi 2 melewati pertandingan pertama yang pertama.
Lalu kami melaporkan nama file
echo $f
.sumber
Mengapa bukan sesuatu yang sederhana seperti:
mengembalikan 0 atau bilangan bulat positif.
egrep -o (Hanya menampilkan kecocokan, trik: beberapa kecocokan pada baris yang sama menghasilkan output multi-baris seolah-olah mereka berada pada baris yang berbeda)
grep -A1 abc
(cetak abc dan garis setelahnya)grep efg | wc -l
(Hitungan 0-n dari garis efg ditemukan setelah abc pada baris yang sama atau mengikuti, hasilnya dapat digunakan dalam 'jika ")grep dapat diubah menjadi egrep dll. jika diperlukan pencocokan pola
sumber
Jika Anda memiliki beberapa perkiraan tentang jarak antara 2 string 'abc' dan 'efg' yang Anda cari, Anda dapat menggunakan:
Dengan begitu, grep pertama akan mengembalikan baris dengan baris 'abc' ditambah # num1 setelahnya, dan # num2 baris setelahnya, dan grep kedua akan menyaring semua yang ada untuk mendapatkan 'efg'. Maka Anda akan tahu di mana file mereka muncul bersama.
sumber
Dengan ugrep dirilis beberapa bulan lalu:
Alat ini sangat dioptimalkan untuk kecepatan. Ini juga kompatibel dengan GNU / BSD / PCRE-grep.
Perhatikan bahwa kita harus menggunakan pengulangan yang malas
+?
, kecuali jika Anda ingin mencocokkan semua barisefg
bersama sampai yang terakhirefg
dalam file.sumber
Ini seharusnya bekerja:
Jika ada lebih dari satu kecocokan, Anda dapat memfilter menggunakan grep -v
sumber