Tüm çizgiye ihtiyacınız yok, sadece normal ifadeden eşleşme


16

Sadece normal bir ifade maç almak gerekir:

$ cat myfile.txt | SOMETHING_HERE "/(\w).+/"

Çıktı, parantez içinde yalnızca eşleşenle aynı olmalıdır.

Grep'i kullanabileceğimi düşünmeyin çünkü tüm çizgiyle eşleşir.

Lütfen bunu nasıl yapacağımı bana bildirin.

Yanıtlar:


13

2 şey:

  • @Rory tarafından belirtildiği gibi, -oseçeneğe ihtiyacınız vardır , bu nedenle sadece eşleşme yazdırılır (tüm satır yerine)
  • Buna ek olarak, Geleceğe bak ve Arkana bak-P gibi yararlı öğeleri içeren Perl düzenli ifadelerini kullanma seçeneğinden de yararlanabilirsiniz, bunlar parçaları arar, ancak aslında eşleşmez ve yazdırmaz. (?= ) (?<= )

Yalnızca parensisin içindeki kısmın eşleşmesini istiyorsanız:

grep -oP '(?<=\/\()\w(?=\).+\/)' myfile.txt

dosyada sokma varsa /(a)5667/, grep 'a' yazacaktır, çünkü:

  • /(tarafından bulunur \/\(, ancak arkalarında (?<= ) oldukları için rapor edilmezler
  • aile eşleştirilir \wve böylece yazdırılır (nedeniyle -o)
  • )5667/b <bulunur \).+\/, ancak ileriye yönelik (?= ) olduklarından rapor edilmezler.

18

İçindeki -oseçeneği kullanın grep.

Örneğin:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar

4
İyi keder ... Bunu sedyapmak için geri başvurularla kaç kez güreştiğim hakkında bir fikrin var mı?
Insyte

10
Grep / egrep için o seçeneği, yalnızca istediği gibi () öğesinde değil, yalnızca normal ifadenin tamamı ile eşleşen öğeyi döndürür.
Kyle Brandt

1
Ancak, bu zaten bilmek çok iyi bir şey :-)
Kyle Brandt

2
@KyleBrandt: Sadece bir parçayı eşleştirmek için (örneğin: parlamentolar) geri kalanını ileriye bir bakışla işaretlemek veya arkasına bakmak mümkündür: (? <=) Ve (? =)
DrYak

7
    sed -n "s/^.*\(captureThis\).*$/\1/p"

-n      don't print lines
s       substitute
^.*     matches anything before the captureThis 
\( \)   capture everything between and assign it to \1 
.*$     matches anything after the captureThis 
\1      replace everything with captureThis 
p       print it

4

Yalnızca parantez içinde ne istiyorsanız, alt eşleşmeleri (Adlandırılmış veya Numaralı Yakalama Grupları) yakalamayı destekleyen bir şeye ihtiyacınız vardır. Ben grep veya egrep bunu yapabilir, perl ve sed yapabilirsiniz sanmıyorum. Örneğin, perl ile:

Foo adlı bir dosyada aşağıdaki gibi bir çizgi varsa:

/adsdds      /

Ve sende yap:

perl -nle 'print $1 if /\/(\w).+\//' foo

A harfi döndürülür. Gerçi istediğin bu olmayabilir. Neyi eşleştirmeye çalıştığınızı söylerseniz, daha iyi yardım alabilirsiniz. İlk parantez kümesinde yakalanan $ 1. 2 $ ikinci set vb. Olur.


Sadece parantez içindeki ile eşleşmeye çalışıyordum. Bir perl ya da bir php betiğine geçmek gibi görünüyor cevap olabilir.
Alex L

4

Siz sorunuzu etiketlenmiş Çünkü bash ek olarak kabuk , yanında başka çözüm yoktur grep :

Bash, =~Perl gibi , operatörü kullanarak sürüm 3.0'dan beri kendi normal ifade motoruna sahiptir .

Şimdi, aşağıdaki kod verildiğinde:

#!/bin/bash
DATA="test <Lane>8</Lane>"

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • Tüm uzantıları almak için bashdeğil, onu çağırmanız gerektiğini unutmayınsh
  • $BASH_REMATCH tüm dizeyi tüm normal ifadeyle eşleştirildiği şekilde verecektir. <Lane>8</Lane>
  • ${BASH_REMATCH[1]} 1. grup tarafından eşleşen kısmı verecektir, bu yüzden sadece 8

Sevgili @ DrYak, umarım burada regex ile XML
ayrışmıyorsunuz

Daha da kötüsü. SANSparallel hızlı büyük ölçekli hizalama yazılımı >tarafından spewed gibi korkunç bir XML ve FASTA veri karışımı (her ikisi de tamamen farklı amaçlar için kullanın ) ayrıştırıyorum . Tabii ki her iki format da kaçmadan aralıklı olarak yazılmıştır. Bu nedenle, buna standart bir XML kitaplığı atmak imkansız. Ve kodun bu noktasında Bash regex kullanıyorum çünkü sadece birkaç veri ayıklamak gerekiyor ve 2 regex bu karmaşa için özel bir ayrıştırıcı yazmaktan çok benim için işi daha iyi yapıyor. Bioinformatik
DrYak

Başka bir deyişle: bir tek regex rathan ile XML tek tek tüm tango dans etmekten daha kolay bir nokta var
DrYak

Hah, yakaladım! :)
joonas.fi

2

Dosyanın içerdiği varsayılarak:

$ cat file
Text-here>xyz</more text

Ve karakter (ler) arasında olmasını istediğiniz >ve </, sen birini kullanabilirsiniz:

grep -oP '.*\K(?<=>)\w+(?=<\/)' file
sed -nE 's:^.*>(\w+)</.*$:\1:p' file
awk '{print(gensub("^.*>(\\w+)</.*$","\\1","g"))}' file
perl -nle 'print $1 if />(\w+)<\//' file

Tümü "xyz" dizesini basacaktır.

Bu satırın rakamlarını yakalamak istiyorsanız:

$ cat file
Text-<here>1234</text>-ends

grep -oP '.*\K(?<=>)[0-9]+(?=<\/)' file
sed -E 's:^.*>([0-9]+)</.*$:\1:' file
awk '{print(gensub(".*>([0-9]+)</.*","\\1","g"))}' file
perl -nle 'print $1 if />([0-9]+)<\//' file


Benim için çok önemli olan sed ile çalışmadığını fark etmekti. Orada [0-9] + kullanmanız için bir neden var. :)
user27432

@ user27423 Gelmez ama POSIX karakter sınıfları ( ağrılı okuma , rahat bir okuma ) yapın: echo 'Text-<here>1234</text>-ends' | sed -E 's|.*>([[:digit:]]+)<.*|\1|'. Bazı durumlarda (örn. [0-9]Vs. [[:digit:]]) okunabilirliğe yardımcı olmazlar, diğerlerinde bence (ör. [ \t\n\r\f\v]Vs. [:space:]).
Samuel Harmer

@SamuelHarmer Ne demek istediğini açıklığa kavuşturabilir misin: Öyle değil mi?
Isaac

@Isaac @ user27432'nin \dçalışmayan karakter grubu hakkındaki yorumundan bahsediyor ve dikkatlerini POSIX karakter sınıflarına çekiyordum.
Samuel Harmer

0

Bu, talep ettiğiniz şeyi başaracak, ama gerçekten istediğiniz şey olduğunu sanmıyorum. .*Maçtan önce herhangi bir şey yemek için normal ifadenin önüne koydum , ama bu açgözlü bir işlem, bu yüzden sadece \wdizedeki sondan bir önceki karakterle eşleşiyor .

Parens ve +.

sed 's/.*\(\w\).\+/\1/' myfile.txt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.