it-swarm-tr.com

Grep çıktısı yalnızca eşleşen gruplamaları mı belirtebilir?

Diyelim ki bir dosyam var:

# file: 'test.txt'
foobar bash 1
bash
foobar happy
foobar

Ben sadece "foobar" sonra hangi kelimeleri göründüğünü bilmek istiyorum, bu yüzden bu regex kullanabilirsiniz:

"foobar \(\w\+\)"

Parantez, foobardan hemen sonra Word'e özel bir ilgim olduğunu gösteriyor. Ama bir grep "foobar \(\w\+\)" test.txt yaptığımda, sadece "foobar'dan sonra Word" yerine tüm normal ifadeyle eşleşen tüm satırları alıyorum:

foobar bash 1
foobar happy

Bu komutun çıktısının şöyle görünmesini tercih ederim:

bash
happy

Grep'e yalnızca normal bir ifadedeki gruplama (veya belirli bir gruplama) ile eşleşen öğeleri çıkarmasını söylemenin bir yolu var mı?

338
Cory Klein

GNU grep, Perl stili normal ifadeler için -P Seçeneğine ve yalnızca desenle eşleşenleri yazdırmak için -o Seçeneğine sahiptir. Bunlar, grep kalıbının bir kısmını -o Amaçlarına uygun olarak belirlenenlerden kaldırmak için etrafa bakma iddiaları ( perlre manpage'de Genişletilmiş Desenler altında açıklanmıştır) kullanılarak birleştirilebilir. .

$ grep -oP 'foobar \K\w+' test.txt
bash
happy
$

\K, (?<=pattern) İfadesinin, çıktısını almak istediğiniz metinden önce sıfır genişliğinde geriye dönük bir onaylama olarak kullandığınız kısa biçimidir (ve daha verimli biçimidir). (?=pattern), Çıktısını almak istediğiniz metnin ardından sıfır genişliğinde ileriye dönük beyan olarak kullanılabilir.

Örneğin, Word'ü foo ve bar arasında eşleştirmek istiyorsanız, şunu kullanabilirsiniz:

$ grep -oP 'foo \K\w+(?= bar)' test.txt

veya (simetri için)

$ grep -oP '(?<=foo )\w+(?= bar)' test.txt
373
camh

Standart grep bunu yapamaz, ancak GNU grep can Son sürümleri sed, awk veya Perl'e dönebilirsiniz. örnek girişinizde istediğiniz gibi; köşe durumlarda biraz farklı davranırlar.

Değiştir foobar Word other stuff by Word, yalnızca değiştirme yapılırsa yazdırın.

sed -n -e 's/^foobar \([[:alnum:]]\+\).*/\1/p'

İlk Word foobar ise, ikinci Word'ü yazdırın.

awk '$1 == "foobar" {print $2}'

İlk Word ise foobar stilini kaldırın ve satırı aksi takdirde atlayın; sonra ilk boşluktan sonra her şeyi soyun ve yazdırın.

Perl -lne 's/^foobar\s+// or next; s/\s.*//; print'
    sed -n "s/^.*foobar\s*\(\S*\).*$/\1/p"

-n     suppress printing
s      substitute
^.*    anything before foobar
foobar initial search match
\s*    any white space character (space)
\(     start capture group
\S*    capture any non-white space character (Word)
\)     end capture group
.*$    anything after the capture group
\1     substitute everything with the 1st capture group
p      print it
46
jgshawkey

Foobar'ın her zaman ilk Kelime veya çizgi olduğunu biliyorsanız, kesim kullanabilirsiniz. Şöyle ki:

grep "foobar" test.file | cut -d" " -f2
19
Dave

pcregrep'in daha akıllısı var -o Seçmek istediğiniz yakalama gruplarını seçmenizi sağlar. Yani, örnek dosyanızı kullanarak,

$ pcregrep -o1 "foobar (\w+)" test.txt
bash
happy

PCRE desteklenmiyorsa, aynı sonucu iki grep çağrısıyla elde edebilirsiniz. Örneğin, foobar sonra Word'ü almak için şunu yapın:

<test.txt grep -o 'foobar  *[^ ]*' | grep -o '[^ ]*$'

Bu, foobar bunun ardından (okunabilirlik için ERE'lerle) sonra rastgele bir Word'e genişletilebilir:

i=1
<test.txt egrep -o 'foobar +([^ ]+ +){'$i'}[^ ]+' | grep -o '[^ ]*$'

Çıktı:

1

i dizininin sıfır tabanlı olduğuna dikkat edin.

9
Thor

grep kullanımı platformlar arası uyumlu değildir, çünkü -P/--Perl-regexp Yalnızca GNU grep , değil BSD grep .

ripgrep çözümünü İşte:

$ rg -o "foobar (\w+)" -r '$1' <test.txt
bash
happy

man rg Uyarınca:

-r/--replace REPLACEMENT_TEXT Her eşleşmeyi verilen metinle değiştirin.

Yakalama grubu dizinleri (örneğin, $5) Ve adlar (örneğin, $foo) Değiştirme dizesinde desteklenir.

İlgili: GH-462 .

7
kenorb

@Jgshawkey yanıtını çok yararlı buldum. grep bunun için iyi bir araç değildir, ancak sed, burada ilgili bir satırı yakalamak için grep kullanan bir örneğimiz var.

Sed 'in regex sözdizimi, alışkın değilseniz, kendine özgüdür.

İşte başka bir örnek: bir kimlik tamsayı almak için xinput çıktısını ayrıştırır

⎜   ↳ SynPS/2 Synaptics TouchPad                id=19   [slave  pointer  (2)]

ve 19 tane istiyorum

export TouchPadID=$(xinput | grep 'TouchPad' | sed  -n "s/^.*id=\([[:digit:]]\+\).*$/\1/p")

Sınıf sözdizimine dikkat edin:

[[:digit:]]

ve aşağıdakilerden kaçma ihtiyacı +

Sadece bir satır eşleşmesi varsayıyorum.

2
Tim Richardson