Çevreleyen karakterleri yazdırmadan önce 'sed' ile eşleşen bir regex çıkartma


24

Dışarıdaki tüm 'sed' doktorlarına:

Bir satırda eşleştirdiği normal ifadeyi çıkarmak için 'sed'i nasıl elde edebilirsiniz?

Başka bir deyişle, sadece sıralı ifadeye karşılık gelen dizgiyi, eşleşen satırdaki tüm eşleşmeyen karakterlerin elimden alınmasını istiyorum.

Aşağıdaki gibi geri referans özelliğini kullanmayı denedim

regular expression to be isolated 
         gets `inserted` 
              here     
               |
               v  
 sed -n 's/.*\( \).*/\1/p 

Bu gibi bazı ifadeler için çalışıyor

 sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*/\1/p 

'CONFIG_ ....' (bazı '* .h' dosyalarında bulunur) ile başlayan tüm makro adlarını düzgün bir şekilde çıkartır ve hepsini satır satır basar

          CONFIG_AT91_GPIO
          CONFIG_DRIVER_AT91EMAC
                   .
                   .   
          CONFIG_USB_ATMEL
          CONFIG_USB_OHCI_NEW
                   .
                 e.t.c. 

AMA yukarıdaki gibi ayrılıyor

  sed -n 's/.*\([0-9][0-9]*\).*/\1/p 

Bu her zaman gibi tek basamak döndürür

                 7
                 9
                 .
                 .  
                 6

gibi bitişik bir sayı alanı çıkarmak yerine.

              8908078
              89670890  
                 .
                 .  
                 .
               23019   
                 .
               e.t.c.  

Not: Bunun “sed” de nasıl elde edildiğine dair geri bildirime minnettar olurum. Bunu 'grep' ve 'awk' ile nasıl yapacağımı biliyorum. Sınırlı da olsa, 'sed' anlayışının içinde delikler olup olmadığını ve sahip olduğum 'sed' de bunu yapmanın bir yolu olup olmadığını öğrenmek istiyorum
. sadece göz ardı.

Yanıtlar:


22

Bir regexp gruplar içerdiğinde, bir dizgenin ona karşı eşleşmesinin birden fazla yolu olabilir: gruplu regexps belirsizdir. Örneğin, regexp ^.*\([0-9][0-9]*\)$ve dizeyi düşünün a12. İki olasılık var:

  • Eşleşme akarşı .*ve 2karşı [0-9]*; 1tarafından eşleştirilir [0-9].
  • Eşleşme a1karşı .*ve boş dize karşı [0-9]*; 2tarafından eşleştirilir [0-9].

Sed, diğer tüm regexp araçları gibi, en eski en uzun eşleşme kuralını uygular: ilk önce değişken uzunluktaki kısmı mümkün olduğunca uzun olan bir dizeyle eşleştirmeye çalışır. Dizenin kalanını regexp'in kalanıyla eşleştirmenin bir yolunu bulursa, sorun yok. Aksi halde sed, ilk değişken uzunluktaki bölüm için bir sonraki en uzun eşleşmeyi dener ve tekrar dener.

Burada ilk önce en uzun dize ile olan a1karşılaşma .*, yani grup sadece eşleşir 2. Grubun daha erken başlamasını istiyorsanız, bazı regexp motorları .*daha az açgözlü olmanıza izin verir , ancak sed'in böyle bir özelliği yoktur. Bu yüzden ek bir çapa ile belirsizliği ortadan kaldırmak gerekir . Liderin .*bir rakamla bitemeyeceğini, böylece grubun ilk rakamının olası ilk eşleşme olacağını belirtin .

  • Rakam grubu satırın başında olamazsa:

    sed -n 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p'
    
  • Rakam grubu satırın başındaysa ve başınız \?operatörü isteğe bağlı parçalar için destekliyorsa :

    sed -n 's/^\(.*[^0-9]\)\?\([0-9][0-9]*\).*/\1/p'
    
  • Rakam grubu satırın başındaysa standart regexp yapılarına bağlı kalarak:

    sed -n -e 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p' -e t -e 's/^\([0-9][0-9]*\).*/\1/p'
    

Bu arada, [0-9]*birinciden sonraki rakamlarla eşleşmeyi sağlayan en eski aynı kuraldır .*.

Bir satırda birden fazla rakam dizisi varsa, programınızın, her zaman en baştaki en uzun eşleşme kuralı nedeniyle, son rakam dizisini çıkartacağını unutmayın .*. İlk basamak sırasını çıkarmak istiyorsanız, daha önce gelenlerin basamak olmayan bir sıra olduğunu belirtmeniz gerekir.

sed -n 's/^[^0-9]*\([0-9][0-9]*\).*$/\1/p'

Daha genel olarak, bir regexp'in ilk karşılaşmasını çıkarmak için, o regexp'in olumsuzluğunu hesaplamanız gerekir. Bu her zaman teorik olarak mümkün olsa da, olumsuzlamanın boyutu, olumsuzladığınız regexp boyutuyla birlikte üssel olarak artar, bu nedenle bu genellikle pratik değildir.

Başka bir örnek düşünün:

sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*/\1/p'

Bu örnek aslında aynı sorunu gösteriyor, ancak tipik girdilerde görmüyorsunuz. Bunu beslemek Eğer hello CONFIG_FOO_CONFIG_BARdışarı baskılar yukarıdaki duruma komut CONFIG_BARdeğil, CONFIG_FOO_CONFIG_BAR.

İlk eşleşmeyi sed ile basmanın bir yolu var, ancak biraz zor:

sed -n -e 's/\(CONFIG_[a-zA-Z0-9_]*\).*/\n\1/' -e T -e 's/^.*\n//' -e p

(Sed'inizin değiştirilen metinde \nyeni bir satır anlamına geldiğini varsayarsak s.) Bu, sed'in regexp'in en erken eşleşmesini aradığı için çalışıyor ve biz CONFIG_…bitten önce gelenleri eşleştirmeye çalışmıyoruz . Satır içinde yeni bir çizgi olmadığından onu geçici bir işaretleyici olarak kullanabiliriz. TKomut önceki eğer vazgeçmek diyor skomut eşleşmedi.

Sed bir şey nasıl yapılacağını çözemiyorsanız, awk çevirin. Aşağıdaki komut, bir regexp'in en eski eşleşmesini yazdırır:

awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'

Ve basit tutmak istiyorsan Perl kullan.

perl -l -ne '/[0-9]+/ && print $&'       # first match
perl -l -ne '/^.*([0-9]+)/ && print $1'  # last match

22

Olmamakla birlikte , bunun sediçin sıklıkla göz ardı edilen şeylerden biri, grep -obence bu görev için daha iyi bir araç.

Örneğin, tüm CONFIG_parametreleri bir çekirdek yapılandırmasından almak istiyorsanız, aşağıdakileri kullanın

# grep -Eo 'CONFIG_[A-Z0-9_]+' config
CONFIG_64BIT
CONFIG_X86_64
CONFIG_X86
CONFIG_INSTRUCTION_DECODER
CONFIG_OUTPUT_FORMAT

Bitişik sayı dizileri almak istiyorsanız:

$ grep -Eo '[0-9]+' foo

7
sed '/\n/P;//!s/[0-9]\{1,\}/\n&\n/;D'

... bu durumu herhangi bir telaşla ortadan kaldıracaktır, ancak nsağ ikame alanındaki s'nin yerine edebi yeni hatlara ihtiyacınız olabilir . Ve bu arada, .*CONFIGiş sadece çizgide bir maç olsaydı işe yarardı - aksi takdirde her zaman sadece sonuncuyu alırdı.

Görebilirsiniz Bu nasıl çalıştığını bir açıklaması için, ama bir satırda oluşur bu çok kez ayrı bir satırda sadece maçı üzerine yazdırılır.

Aynı stratejiyi [num]bir satıra yerleştirmek için aynı stratejiyi kullanabilirsiniz . Örneğin, CONFIG eşleşmesini sadece bir satırdaki üçüncüyse yazdırmak istiyorsanız:

sed '/\n/P;//d;s/CONFIG[[:alnum:]]*/\n&\n/3;D'

... ancak bu CONFIG, her bir olay için dizelerin en az bir alfasayısal olmayan karakterle ayrıldığını varsayar .

Sanırım - sayı için - bu da işe yarayacak:

sed -n 's/[^0-9]\{1,\}/\n/g;s/\n*\(.*[0-9]\).*/\1/p

... sağ taraftaki gibi aynı ihtirasla \n. Bu, ilkinden bile daha hızlı olacaktı ancak genel olarak açıkça uygulanamaz.

CONFIG için P;...;Dyukarıdaki döngüyü deseninizle birlikte kullanabilirsiniz ya da şunları yapabilirsiniz:

sed -n 's/[^C]*\(CONFIG[[:alnum:]]*\)\{0,1\}C\{0,1\}/\1\n/g;s/\(\n\)*/\1/g;/C/s/.$//p'

... bu sadece biraz daha fazla ilgili ve sedreferans önceliğini doğru sıralayarak çalışıyor . Ayrıca, bir satırdaki tüm CONFIG eşleşmelerini tek seferde izole eder - daha önce olduğu gibi aynı varsayımı yapar - her CONFIG eşleşmesinin en az bir alfasayısal olmayan karakterle ayrılacağı varsayılır. GNU ile sedyazabilirsiniz:

sed -En 's/[^C]*(CONFIG\w*)?C?/\1\n/g;s/(\n)*/\1/g;/C/s/.$//p'
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.