Dengan grep, bagaimana saya bisa mencocokkan suatu pola dan membalikkan kecocokan dengan pola lain?


11

Dengan grep, saya ingin memilih semua garis yang cocok dengan pola, dan yang tidak cocok dengan pola lain. Saya ingin dapat menggunakan doa tunggal grepsehingga saya dapat menggunakan --after-contextopsi (atau --before-context, atau --context).

-vtidak layak di sini, karena meniadakan semua pola yang saya berikan untuk grepmenggunakan -eopsi.

Contoh

Saya ingin mencari garis yang cocok needle, mengabaikan garis yang cocok ignore me, dengan satu baris konteks berikut.

Ini file input saya:

one needle ignore me
two
three
four needle
five

Output yang saya inginkan adalah:

four needle
five

Seperti yang Anda lihat, solusi naif ini tidak berfungsi:

$ cat file | grep --after-context=1 needle | grep -v 'ignore me'
two
---
four needle
five

Jawaban:


10

Jika Anda memiliki GNU grep, Anda dapat menggunakan ekspresi reguler Perl , yang memiliki konstruksi negasi .

grep -A1 -P '^(?!.*ignore me).*needle'

Jika Anda tidak memiliki grep GNU, Anda dapat meniru opsi konteks sebelum / sesudah dalam awk .

awk -v after=3 -v before=2 '
/needle/ && !/ignore me/ {
    for (i in h) {
        print h[i];
        delete h[i];
    }
    until = NR + after;
}
{
    if (NR <= until) print $0; else h[NR] = $0;
    delete h[NR-before];
}
END {exit !until}
'

8

Anda tampaknya menggunakan GNU . Dengan GNU grep, Anda bisa memberikan --perl-regextanda untuk mengaktifkan PCRE dan kemudian memberikan pernyataan lookahead negatif, contoh di bawah

grep --after-context=1 \
--perl-regex '^(?:(?!ignore me).)*needle(?:(?!ignore me).)*$' file.txt
four needle
five

Hal utama yang perlu diperhatikan di sini adalah bahwa (?:(?!STRING).)*adalah STRINGsebagaimana [^CHAR]*adanyaCHAR


@ 1_CR ... Pak .. luar biasa ..: P ada sesuatu yang lebih menyenangkan dariack
Rahul Patil

@RahulPatil. :-), ya GNU grep itu bagus.
iruvar

Bukan itu yang saya inginkan. Saya ingin itu berfungsi apakah "abaikan saya" sebelum atau sesudah "jarum".
Flimm

@RahulPatil, terima kasih, saya memperbaikinya dalam versi terbaru
iruvar

Sangat berguna. Terutama dalam kasus grep dengan konteks di mana Anda ingin mengecualikan garis yang cocok tetapi tanpa bagian tertentu dari pola. Dekat dengan pertanyaan awal tetapi tidak persis sama.
gaoithe

2

Saya akan menyarankan menggunakan awk sebagai gantinya karena menangani multi-line IO lebih baik. Entah 1) Pipa hasilnya ke GNU awk dengan --\nsebagai pemisah rekaman, atau 2) Lakukan semua pencocokan dalam awk.

Pilihan 1

<file grep -A1 needle | awk '!/ignore me/' RS='--\n' ORS='--\n'

Keluaran:

four needle                                                                                  
five
--

Catatan, opsi ini mencari seluruh catatan untuk ignore me, mengatur FS=1dan mencocokkan $1dengan hanya membandingkan dengan baris pertama.

pilihan 2

<file awk 'a-- > 0; $0 ~ re1 && $0 !~ re2 { print $0; a=after }' re1=needle re2='ignore me' after=1

Apakah ada banyak ignore mefile, awk tidak berfungsi
Rahul Patil

@RahulPatil: dapatkah Anda ulangi atau menambahkan lebih banyak detail ke pertanyaan Anda? Saya tidak mengerti apa yang Anda minta.
Thor

@Thos menguji contoh Anda dengan file input ini paste.ubuntu.com/6252860
Rahul Patil

@RahulPatil: Saya mengerti maksud Anda sekarang, Opsi 1 mengasumsikan bahwa --\npembatas adalah di antara masing-masing grup yang cocok, yang tidak ada di sana jika grup berdekatan satu sama lain. Bagaimana kelompok yang berdekatan harus ditangani adalah tugas khusus, jadi ini tidak selalu salah. Opsi 2 tidak tergantung pada pemisah dan tidak terpengaruh.
Thor
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.